/** ** Work for missile hit. */ void Missile::MissileHit(CUnit *unit) { const MissileType &mtype = *this->Type; if (mtype.ImpactSound.Sound) { PlayMissileSound(*this, mtype.ImpactSound.Sound); } const PixelPos pixelPos = this->position + this->Type->size / 2; // // The impact generates a new missile. // if (mtype.Impact.empty() == false) { for (std::vector<MissileConfig *>::const_iterator it = mtype.Impact.begin(); it != mtype.Impact.end(); ++it) { const MissileConfig &mc = **it; Missile *impact = MakeMissile(*mc.Missile, pixelPos, pixelPos); if (impact && impact->Type->Damage) { impact->SourceUnit = this->SourceUnit; } } } if (mtype.ImpactParticle) { mtype.ImpactParticle->pushPreamble(); mtype.ImpactParticle->pushInteger(pixelPos.x); mtype.ImpactParticle->pushInteger(pixelPos.y); mtype.ImpactParticle->run(); } if (!this->SourceUnit) { // no owner - green-cross ... return; } const Vec2i pos = Map.MapPixelPosToTilePos(pixelPos); if (!Map.Info.IsPointOnMap(pos)) { // FIXME: this should handled by caller? DebugPrint("Missile gone outside of map!\n"); return; // outside the map. } // // Choose correct goal. // if (unit) { if (unit->Destroyed) { return; } if (mtype.Pierce && mtype.PierceOnce) { if (IsPiercedUnit(*this, *unit)) { return; } else { PiercedUnits.insert(this->PiercedUnits.begin(), unit); } } MissileHitsGoal(*this, *unit, 1); return; } if (!mtype.Range) { if (this->TargetUnit && (mtype.FriendlyFire == false || this->TargetUnit->Player->Index != this->SourceUnit->Player->Index)) { // // Missiles without range only hits the goal always. // CUnit &goal = *this->TargetUnit; if (mtype.Pierce && mtype.PierceOnce) { if (IsPiercedUnit(*this, goal)) { return; } else { PiercedUnits.insert(this->PiercedUnits.begin(), &goal); } } if (goal.Destroyed) { this->TargetUnit = NULL; return; } MissileHitsGoal(*this, goal, 1); return; } MissileHitsWall(*this, pos, 1); return; } { // // Hits all units in range. // const Vec2i range(mtype.Range - 1, mtype.Range - 1); std::vector<CUnit *> table; Select(pos - range, pos + range, table); Assert(this->SourceUnit != NULL); for (size_t i = 0; i != table.size(); ++i) { CUnit &goal = *table[i]; // // Can the unit attack this unit-type? // NOTE: perhaps this should be come a property of the missile. // Also check CorrectSphashDamage so land explosions can't hit the air units // if (CanTarget(*this->SourceUnit->Type, *goal.Type) && (mtype.FriendlyFire == false || goal.Player->Index != this->SourceUnit->Player->Index)) { bool shouldHit = true; if (mtype.Pierce && mtype.PierceOnce) { if (IsPiercedUnit(*this, goal)) { shouldHit = false; } else { PiercedUnits.insert(this->PiercedUnits.begin(), &goal); } } if (mtype.CorrectSphashDamage == true) { bool isPositionSpell = false; if (this->TargetUnit == NULL && this->SourceUnit->CurrentAction() == UnitActionSpellCast) { const COrder_SpellCast &order = *static_cast<COrder_SpellCast *>(this->SourceUnit->CurrentOrder()); if (order.GetSpell().Target == TargetPosition) { isPositionSpell = true; } } if (isPositionSpell || this->SourceUnit->CurrentAction() == UnitActionAttackGround) { if (goal.Type->UnitType != this->SourceUnit->Type->UnitType) { shouldHit = false; } } else { if (this->TargetUnit == NULL || goal.Type->UnitType != this->TargetUnit->Type->UnitType) { shouldHit = false; } } } if (shouldHit) { int splash = goal.MapDistanceTo(pos); if (splash) { splash *= mtype.SplashFactor; } else { splash = 1; } MissileHitsGoal(*this, goal, splash); } } } } // Missile hits ground. const Vec2i offset(mtype.Range, mtype.Range); const Vec2i posmin = pos - offset; for (int i = mtype.Range * 2; --i;) { for (int j = mtype.Range * 2; --j;) { const Vec2i posIt(posmin.x + i, posmin.y + j); if (Map.Info.IsPointOnMap(posIt)) { int d = Distance(pos, posIt); d *= mtype.SplashFactor; if (d == 0) { d = 1; } MissileHitsWall(*this, posIt, d); } } } }
/** ** Work for missile hit. ** ** @param missile Missile reaching end-point. */ void MissileHit(Missile *missile) { CUnit *goal; int x; int y; CUnit *table[UnitMax]; int n; int i; int splash; if (missile->Type->ImpactSound.Sound) { PlayMissileSound(missile, missile->Type->ImpactSound.Sound); } x = missile->X + missile->Type->Width / 2; y = missile->Y + missile->Type->Height / 2; // // The impact generates a new missile. // if (missile->Type->ImpactMissile) { MakeMissile(missile->Type->ImpactMissile, x, y, x, y); } if (missile->Type->ImpactParticle) { missile->Type->ImpactParticle->pushPreamble(); missile->Type->ImpactParticle->pushInteger(x); missile->Type->ImpactParticle->pushInteger(y); missile->Type->ImpactParticle->run(); } if (!missile->SourceUnit) { // no owner - green-cross ... return; } x /= TileSizeX; y /= TileSizeY; if (x < 0 || y < 0 || x >= Map.Info.MapWidth || y >= Map.Info.MapHeight) { // FIXME: this should handled by caller? DebugPrint("Missile gone outside of map!\n"); return; // outside the map. } // // Choose correct goal. // if (!missile->Type->Range) { if (missile->TargetUnit) { // // Missiles without range only hits the goal always. // goal = missile->TargetUnit; if (goal->Destroyed) { // Destroyed goal->RefsDecrease(); missile->TargetUnit = NoUnitP; return; } MissileHitsGoal(missile, goal, 1); return; } return; } // // Hits all units in range. // i = missile->Type->Range; n = UnitCache.Select(x - i + 1, y - i + 1, x + i, y + i, table, UnitMax); Assert(missile->SourceUnit != NULL); for (i = 0; i < n; ++i) { goal = table[i]; // // Can the unit attack the this unit-type? // NOTE: perhaps this should be come a property of the missile. // if (CanTarget(missile->SourceUnit->Type, goal->Type)) { splash = MapDistanceToUnit(x, y, goal); if (splash) { splash *= missile->Type->SplashFactor; } else { splash = 1; } MissileHitsGoal(missile, goal, splash); } } }