/** ** Calculate parabolic trajectories. ** ** @param missile Missile pointer. ** ** @return 1 if target is reached, 0 otherwise ** ** @todo Find good values for ZprojToX and Y */ static int ParabolicMissile(Missile &missile) { int k; // Coefficient of the parabol. int zprojToX; // Projection of Z axis on axis X. int zprojToY; // Projection of Z axis on axis Y. int z; // should be missile.Z later. k = -2048; //-1024; // Should be initialised by an other method (computed with distance...) zprojToX = 4; zprojToY = 1024; if (MissileInitMove(missile) == 1) { return 1; } Assert(missile.Type != NULL); const PixelPos orig_pos = missile.position; Assert(missile.TotalStep != 0); const PixelPos diff = (missile.destination - missile.source); missile.position = missile.source + diff * missile.CurrentStep / missile.TotalStep; Assert(k != 0); z = missile.CurrentStep * (missile.TotalStep - missile.CurrentStep) / k; // Until Z is used for drawing, modify X and Y. missile.position.x += z * zprojToX / 64; missile.position.y += z * zprojToY / 64; missile.MissileNewHeadingFromXY(missile.position - orig_pos); if (missile.Type->Smoke.Missile && missile.CurrentStep) { const PixelPos position = missile.position + missile.Type->size / 2; MakeMissile(*missile.Type->Smoke.Missile, position, position); } if (missile.Type->SmokeParticle && missile.CurrentStep) { const PixelPos position = missile.position + missile.Type->size / 2; missile.Type->SmokeParticle->pushPreamble(); missile.Type->SmokeParticle->pushInteger(position.x); missile.Type->SmokeParticle->pushInteger(position.y); missile.Type->SmokeParticle->run(); } if (missile.Type->Pierce) { MissileHandlePierce(missile, Map.MapPixelPosToTilePos(missile.position)); } return 0; }
/** ** Init the move. ** ** @param missile missile to initialise for movement. ** ** @return true if goal is reached, false else. */ bool MissileInitMove(Missile &missile) { const PixelPos heading = missile.destination - missile.position; missile.MissileNewHeadingFromXY(heading); if (!(missile.State & 1)) { missile.CurrentStep = 0; missile.TotalStep = 0; if (heading.x == 0 && heading.y == 0) { return true; } // initialize missile.TotalStep = Distance(missile.source, missile.destination); missile.State++; return false; } Assert(missile.TotalStep != 0); missile.CurrentStep += missile.Type->Speed; if (missile.CurrentStep >= missile.TotalStep) { missile.position = missile.destination; return true; } return false; }
/** ** Handle point to point missile. ** ** @param missile Missile pointer. ** ** @return true if goal is reached, false else. */ bool PointToPointMissile(Missile &missile) { MissileInitMove(missile); if (missile.TotalStep == 0) { return true; } Assert(missile.Type != NULL); Assert(missile.TotalStep != 0); const PixelPos diff = (missile.destination - missile.source); const PixelPrecise sign(diff.x >= 0 ? 1.0 : -1.0, diff.y >= 0 ? 1.0 : -1.0); // Remember sign to move into correct direction const PixelPrecise oldPos((double)missile.position.x, (double)missile.position.y); // Remember old position PixelPrecise pos(oldPos); missile.position = missile.source + diff * missile.CurrentStep / missile.TotalStep; for (; pos.x * sign.x <= missile.position.x * sign.x && pos.y * sign.y <= missile.position.y * sign.y; pos.x += (double)diff.x * missile.Type->SmokePrecision / missile.TotalStep, pos.y += (double)diff.y * missile.Type->SmokePrecision / missile.TotalStep) { const PixelPos position((int)pos.x + missile.Type->size.x / 2, (int)pos.y + missile.Type->size.y / 2); if (missile.Type->Smoke.Missile && (missile.CurrentStep || missile.State > 1)) { Missile *smoke = MakeMissile(*missile.Type->Smoke.Missile, position, position); if (smoke && smoke->Type->NumDirections > 1) { smoke->MissileNewHeadingFromXY(diff); } } if (missile.Type->SmokeParticle && (missile.CurrentStep || missile.State > 1)) { missile.Type->SmokeParticle->pushPreamble(); missile.Type->SmokeParticle->pushInteger(position.x); missile.Type->SmokeParticle->pushInteger(position.y); missile.Type->SmokeParticle->run(); } if (missile.Type->Pierce) { const PixelPos posInt((int)pos.x, (int)pos.y); MissileHandlePierce(missile, Map.MapPixelPosToTilePos(posInt)); } } // Handle wall blocking and kill first enemy for (pos = oldPos; pos.x * sign.x <= missile.position.x * sign.x && pos.y * sign.y <= missile.position.y * sign.y; pos.x += (double)diff.x / missile.TotalStep, pos.y += (double)diff.y / missile.TotalStep) { const PixelPos position((int)pos.x + missile.Type->size.x / 2, (int)pos.y + missile.Type->size.y / 2); const Vec2i tilePos(Map.MapPixelPosToTilePos(position)); if (Map.Info.IsPointOnMap(tilePos) && MissileHandleBlocking(missile, position)) { return true; } if (missile.Type->MissileStopFlags) { if (!Map.Info.IsPointOnMap(tilePos)) { // gone outside missile.TTL = 0; return false; } const CMapField &mf = *Map.Field(tilePos); if (missile.Type->MissileStopFlags & mf.Flags) { // incompatible terrain missile.position = position; missile.MissileHit(); missile.TTL = 0; return false; } } } if (missile.CurrentStep == missile.TotalStep) { missile.position = missile.destination; return true; } return false; }
/* virtual */ void CAnimation_SpawnMissile::Action(CUnit &unit, int &/*move*/, int /*scale*/) const { Assert(unit.Anim.Anim == this); const int startx = ParseAnimInt(&unit, this->startXStr.c_str()); const int starty = ParseAnimInt(&unit, this->startYStr.c_str()); const int destx = ParseAnimInt(&unit, this->destXStr.c_str()); const int desty = ParseAnimInt(&unit, this->destYStr.c_str()); const int flags = ParseAnimFlags(unit, this->flagsStr.c_str()); const int offsetnum = ParseAnimInt(&unit, this->offsetNumStr.c_str()); const CUnit *goal = flags & ANIM_SM_RELTARGET ? unit.CurrentOrder()->GetGoal() : &unit; const int dir = ((goal->Direction + NextDirection / 2) & 0xFF) / NextDirection; const PixelPos moff = goal->Type->MissileOffsets[dir][!offsetnum ? 0 : offsetnum - 1]; PixelPos start; PixelPos dest; MissileType *mtype = MissileTypeByIdent(this->missileTypeStr); if (mtype == NULL) { return; } if (!goal || goal->Destroyed) { return; } if ((flags & ANIM_SM_PIXEL)) { start.x = goal->tilePos.x * PixelTileSize.x + goal->IX + moff.x + startx; start.y = goal->tilePos.y * PixelTileSize.y + goal->IY + moff.y + starty; } else { start.x = (goal->tilePos.x + startx) * PixelTileSize.x + PixelTileSize.x / 2 + moff.x; start.y = (goal->tilePos.y + starty) * PixelTileSize.y + PixelTileSize.y / 2 + moff.y; } if ((flags & ANIM_SM_TOTARGET)) { CUnit *target = goal->CurrentOrder()->GetGoal(); if (!target || target->Destroyed) { Assert(!mtype->AlwaysFire || mtype->Range); if (!target && mtype->AlwaysFire == false) { return; } } if (!target) { if (goal->CurrentAction() == UnitActionAttack || goal->CurrentAction() == UnitActionAttackGround) { COrder_Attack &order = *static_cast<COrder_Attack *>(goal->CurrentOrder()); dest = Map.TilePosToMapPixelPos_Center(order.GetGoalPos()); } else if (goal->CurrentAction() == UnitActionSpellCast) { COrder_SpellCast &order = *static_cast<COrder_SpellCast *>(goal->CurrentOrder()); dest = Map.TilePosToMapPixelPos_Center(order.GetGoalPos()); } if (flags & ANIM_SM_PIXEL) { dest.x += destx; dest.y += desty; } else { dest.x += destx * PixelTileSize.x; dest.y += desty * PixelTileSize.y; } } else if (flags & ANIM_SM_PIXEL) { dest.x = target->GetMapPixelPosCenter().x + destx; dest.y = target->GetMapPixelPosCenter().y + desty; } else { dest.x = (target->tilePos.x + destx) * PixelTileSize.x; dest.y = (target->tilePos.y + desty) * PixelTileSize.y; dest += target->Type->GetPixelSize() / 2; } } else { if ((flags & ANIM_SM_PIXEL)) { dest.x = goal->GetMapPixelPosCenter().x + destx; dest.y = goal->GetMapPixelPosCenter().y + desty; } else { dest.x = (goal->tilePos.x + destx) * PixelTileSize.x; dest.y = (goal->tilePos.y + desty) * PixelTileSize.y; dest += goal->Type->GetPixelSize() / 2; } } Vec2i destTilePos = Map.MapPixelPosToTilePos(dest); const int dist = goal->MapDistanceTo(destTilePos); if ((flags & ANIM_SM_RANGED) && !(flags & ANIM_SM_PIXEL) && dist > goal->Stats->Variables[ATTACKRANGE_INDEX].Max && dist < goal->Type->MinAttackRange) { } else { Missile *missile = MakeMissile(*mtype, start, dest); if (flags & ANIM_SM_SETDIRECTION) { PixelPos posd; posd.x = Heading2X[goal->Direction / NextDirection]; posd.y = Heading2Y[goal->Direction / NextDirection]; missile->MissileNewHeadingFromXY(posd); } if (flags & ANIM_SM_DAMAGE) { missile->SourceUnit = &unit; } if (flags & ANIM_SM_TOTARGET) { missile->TargetUnit = goal->CurrentOrder()->GetGoal(); } } }
/** ** Calculate parabolic trajectories. ** ** @param missile Missile pointer. ** ** @return true if target is reached, false otherwise ** ** @todo Find good values for ZprojToX and Y */ static bool ParabolicMissile(Missile &missile) { // Should be initialised by an other method (computed with distance...) const double k = -missile.Type->ParabolCoefficient; // Coefficient of the parabol. const double zprojToX = 4.0; // Projection of Z axis on axis X. const double zprojToY = 1024.0; // Projection of Z axis on axis Y. double z; // should be missile.Z later. MissileInitMove(missile); if (missile.TotalStep == 0) { return true; } Assert(missile.Type != NULL); const PixelPos orig_pos = missile.position; Assert(missile.TotalStep != 0); const PixelPos diff = (missile.destination - missile.source); const PixelPrecise sign(diff.x >= 0 ? 1 : -1, diff.y >= 0 ? 1 : -1); // Remember sign to move into correct direction PixelPrecise pos(missile.position.x, missile.position.y); // Remember old position missile.position = missile.source + diff * missile.CurrentStep / missile.TotalStep; Assert(k != 0); z = (double)missile.CurrentStep * (missile.TotalStep - missile.CurrentStep) / k; // Until Z is used for drawing, modify X and Y. missile.position.x += (int)(z * zprojToX / 64.0); missile.position.y += (int)(z * zprojToY / 64.0); missile.MissileNewHeadingFromXY(missile.position - orig_pos); for (; pos.x * sign.x <= missile.position.x * sign.x && pos.y * sign.y <= missile.position.y * sign.y; pos.x += (double)diff.x * missile.Type->SmokePrecision / missile.TotalStep, pos.y += (double)diff.y * missile.Type->SmokePrecision / missile.TotalStep) { if (missile.Type->Smoke.Missile && missile.CurrentStep) { const PixelPos position((int)pos.x + missile.Type->size.x / 2, (int)pos.y + missile.Type->size.y / 2); Missile *smoke = MakeMissile(*missile.Type->Smoke.Missile, position, position); if (smoke && smoke->Type->NumDirections > 1) { smoke->MissileNewHeadingFromXY(diff); } } if (missile.Type->SmokeParticle && missile.CurrentStep) { const PixelPos position((int)pos.x + missile.Type->size.x / 2, (int)pos.y + missile.Type->size.y / 2); missile.Type->SmokeParticle->pushPreamble(); missile.Type->SmokeParticle->pushInteger(position.x); missile.Type->SmokeParticle->pushInteger(position.y); missile.Type->SmokeParticle->run(); } if (missile.Type->Pierce) { const PixelPos position((int)pos.x, (int)pos.y); MissileHandlePierce(missile, Map.MapPixelPosToTilePos(position)); } } if (missile.CurrentStep == missile.TotalStep) { missile.position = missile.destination; return true; } return false; }