/** ** Missile hits the goal. ** ** @param missile Missile hitting the goal. ** @param goal Goal of the missile. ** @param splash Splash damage divisor. */ static void MissileHitsGoal(const Missile &missile, CUnit &goal, int splash) { if (!missile.Type->CanHitOwner && missile.SourceUnit == &goal) { return; } if (goal.CurrentAction() != UnitActionDie) { int damage; if (missile.Type->Damage) { // custom formula Assert(missile.SourceUnit != NULL); damage = CalculateDamage(*missile.SourceUnit, goal, missile.Type->Damage) / splash; } else if (missile.Damage) { // direct damage, spells mostly damage = missile.Damage / splash; } else { Assert(missile.SourceUnit != NULL); damage = CalculateDamage(*missile.SourceUnit, goal, Damage) / splash; } if (missile.Type->Pierce) { // Handle pierce factor for (size_t i = 0; i < (missile.PiercedUnits.size() - 1); ++i) { damage *= (double)missile.Type->ReduceFactor / 100; } } HitUnit(missile.SourceUnit, goal, damage, &missile); } }
/** ** Missile hits the goal. ** ** @param missile Missile hitting the goal. ** @param goal Goal of the missile. ** @param splash Splash damage divisor. */ static void MissileHitsGoal(const Missile &missile, CUnit &goal, int splash) { if (!missile.Type->CanHitOwner && missile.SourceUnit == &goal) { return; } //Wyrmgus start if (CalculateHit(*missile.SourceUnit, *goal.Stats, &goal) == false) { if (splash == 1 && missile.Type->SplashFactor == 0) { return; } else if (splash == 1 && missile.Type->SplashFactor > 0) { splash = missile.Type->SplashFactor; // if missile has splash factor but missed, apply splash damage } } //Wyrmgus end if (goal.CurrentAction() != UnitActionDie) { int damage; if (missile.Type->Damage) { // custom formula Assert(missile.SourceUnit != NULL); damage = CalculateDamage(*missile.SourceUnit, goal, missile.Type->Damage) / splash; } else if (missile.Damage) { // direct damage, spells mostly damage = missile.Damage / splash; } else { Assert(missile.SourceUnit != NULL); damage = CalculateDamage(*missile.SourceUnit, goal, Damage) / splash; } if (missile.Type->Pierce) { // Handle pierce factor for (size_t i = 0; i < (missile.PiercedUnits.size() - 1); ++i) { damage *= (double)missile.Type->ReduceFactor / 100; } } HitUnit(missile.SourceUnit, goal, damage, &missile); //Wyrmgus start //apply Thorns damage if attacker is at melee range if (&goal && goal.Variable[THORNSDAMAGE_INDEX].Value && missile.SourceUnit->MapDistanceTo(goal) <= 1) { int thorns_damage = std::max<int>(goal.Variable[THORNSDAMAGE_INDEX].Value - missile.SourceUnit->Variable[ARMOR_INDEX].Value, 1); if (GameSettings.NoRandomness) { thorns_damage -= ((thorns_damage + 2) / 2) / 2; //if no randomness setting is used, then the damage will always return what would have been the average damage with randomness } else { thorns_damage -= SyncRand() % ((thorns_damage + 2) / 2); } HitUnit(&goal, *missile.SourceUnit, thorns_damage); } //Wyrmgus end } }
void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* damageInfo) { damageInfo->attacker = this; damageInfo->target = victim; damageInfo->damage = 0; damageInfo->hitType = HIT_MISS; if (!victim) return; if (!IsAlive() || !victim->IsAlive()) return; damage += CalculateDamage(); damageInfo->damage = damage; damageInfo->hitType = RollHitType(damageInfo->target); switch (damageInfo->hitType) { case HIT_MISS: damageInfo->damage = 0; break; case HIT_CRITICAL: damageInfo->damage += damageInfo->damage; break; case HIT_NORMAL: default: break; } }
/** ** Fire missile. */ global void FireMissile(Unit* unit) { int x; int y; int dx; int dy; Unit* goal; Missile* missile; DebugLevel3("%s:\n",__FUNCTION__); if( unit->Type->MissileWeapon==MissileNone ) { // FIXME: must hit now!!! if( !(goal=unit->Command.Data.Move.Goal) ) { DebugLevel1("Missile-none hits no unit, shouldn't happen!\n"); return; } // FIXME: make sure thats the correct unit. if( !goal->HP || goal->Command.Action==UnitActionDie ) { return; } HitUnit(goal,CalculateDamage(unit->Stats,goal)); return; } x=unit->X*TileSizeX+TileSizeX/2; y=unit->Y*TileSizeY+TileSizeY/2; if( (goal=unit->Command.Data.Move.Goal) ) { // Fire to nearest point of unit! if( goal->Type ) { NearestOfUnit(goal,unit->X,unit->Y,&dx,&dy); } else { // FIXME: unit killed? dx=goal->X; dy=goal->Y; } DebugLevel3("Fire to unit at %d,%d\n",dx,dy); dx=dx*TileSizeX+TileSizeX/2; dy=dy*TileSizeY+TileSizeY/2; } else { dx=unit->Command.Data.Move.DX*TileSizeX+TileSizeX/2; dy=unit->Command.Data.Move.DY*TileSizeY+TileSizeY/2; } missile=MakeMissile(unit->Type->MissileWeapon,x,y,dx,dy); // // Damage of missile // // missile->Damage=CalculateDamage(unit,goal); missile->SourceUnit=unit; missile->SourceType=unit->Type; missile->SourceStats=unit->Stats; missile->SourcePlayer=unit->Player; }
/** ** Work for missile hit. */ global void MissileHit(int missile) { Unit* goal; // FIXME: should I move the PlayMissileSound to here? // FIXME: And should the the connected missile be defined in the missile // FIXME: structure switch( Missiles[missile].Type->Type ) { case MissileArrow: case MissileAxe: PlayMissileSound(Missiles+missile, Missiles[missile].Type->ImpactSound.Sound); break; case MissileBallistaBolt: case MissileBigCannon: PlayMissileSound(Missiles+missile, Missiles[missile].Type->ImpactSound.Sound); MakeMissile(MissileImpact ,Missiles[missile].X ,Missiles[missile].Y ,0,0); break; case MissileSubmarineMissile: case MissileTurtleMissile: PlayMissileSound(Missiles+missile, Missiles[missile].Type->ImpactSound.Sound); MakeMissile(MissileImpact ,Missiles[missile].X ,Missiles[missile].Y ,0,0); break; case MissileGreenCross: break; } if( !Missiles[missile].SourceType ) { return; } // FIXME: must choose better goal! // FIXME: what can the missile hit? goal=UnitOnMapTile(Missiles[missile].X/TileSizeX ,Missiles[missile].Y/TileSizeY); if( !goal || !goal->HP ) { return; } HitUnit(goal,CalculateDamage(Missiles[missile].SourceStats,goal)); }
/** ** Missile hits the goal. ** ** @param missile Missile hitting the goal. ** @param goal Goal of the missile. ** @param splash Splash damage divisor. */ static void MissileHitsGoal(const Missile *missile, CUnit *goal, int splash) { if (!missile->Type->CanHitOwner && goal == missile->SourceUnit) { return; } if (goal->Orders[0]->Action != UnitActionDie) { if (missile->Damage) { // direct damage, spells mostly HitUnit(missile->SourceUnit, goal, missile->Damage / splash); } else { Assert(missile->SourceUnit != NULL); HitUnit(missile->SourceUnit, goal, CalculateDamage(missile->SourceUnit, goal) / splash); } } }
void Spell::HandleEffects(Unit* unitTarget, uint32 i) { // if the effect has been handled we skip it if (m_effectMask & (1 << i)) return; uint8 eff = m_spellInfo->Effect[i]; printf("Spell: %u Effect: %u\n", m_spellInfo->Id, eff); damage = CalculateDamage(i); if (eff < TOTAL_SPELL_EFFECTS) { (this->*SpellEffects[eff])((SpellEffIndex)i); } }
/** ** Fire missile. ** ** @param unit Unit that fires the missile. */ void FireMissile(CUnit &unit, CUnit *goal, const Vec2i &goalPos) { Vec2i newgoalPos = goalPos; // Goal dead? if (goal) { Assert(!unit.Type->Missile.Missile->AlwaysFire || unit.Type->Missile.Missile->Range); if (goal->Destroyed) { DebugPrint("destroyed unit\n"); return; } if (goal->Removed) { return; } if (goal->CurrentAction() == UnitActionDie) { if (unit.Type->Missile.Missile->AlwaysFire) { newgoalPos = goal->tilePos; goal = NULL; } else { return; } } } // No missile hits immediately! if (unit.Type->Missile.Missile->Class == MissileClassNone) { // No goal, take target coordinates if (!goal) { if (Map.WallOnMap(goalPos)) { if (Map.HumanWallOnMap(goalPos)) { Map.HitWall(goalPos, CalculateDamageStats(*unit.Stats, *UnitTypeHumanWall->Stats, unit.Variable[BLOODLUST_INDEX].Value)); } else { Map.HitWall(goalPos, CalculateDamageStats(*unit.Stats, *UnitTypeOrcWall->Stats, unit.Variable[BLOODLUST_INDEX].Value)); } return; } DebugPrint("Missile-none hits no unit, shouldn't happen!\n"); return; } HitUnit(&unit, *goal, CalculateDamage(unit, *goal)); return; } // If Firing from inside a Bunker CUnit *from = GetFirstContainer(unit); const int dir = ((unit.Direction + NextDirection / 2) & 0xFF) / NextDirection; const PixelPos startPixelPos = Map.TilePosToMapPixelPos_Center(from->tilePos) + unit.Type->MissileOffsets[dir][0]; Vec2i dpos; if (goal) { Assert(goal->Type); // Target invalid? // Moved out of attack range? if (unit.MapDistanceTo(*goal) < unit.Type->MinAttackRange) { DebugPrint("Missile target too near %d,%d\n" _C_ unit.MapDistanceTo(*goal) _C_ unit.Type->MinAttackRange); // FIXME: do something other? return; } // Fire to nearest point of the unit! // If Firing from inside a Bunker if (unit.Container) { NearestOfUnit(*goal, GetFirstContainer(unit)->tilePos, &dpos); } else { dpos = goal->tilePos + goal->Type->GetHalfTileSize(); } } else { dpos = newgoalPos; // FIXME: Can this be too near?? } PixelPos destPixelPos = Map.TilePosToMapPixelPos_Center(dpos); Missile *missile = MakeMissile(*unit.Type->Missile.Missile, startPixelPos, destPixelPos); // // Damage of missile // if (goal) { missile->TargetUnit = goal; } missile->SourceUnit = &unit; }
void Compute(CUnit *const dest) { const CPlayer &player = *attacker->Player; if (!dest->IsVisibleAsGoal(player)) { dest->CacheLock = 1; return; } const CUnitType &type = *attacker->Type; const CUnitType &dtype = *dest->Type; // won't be a target... if (!CanTarget(type, dtype)) { // can't be attacked. dest->CacheLock = 1; return; } // Don't attack invulnerable units if (dtype.BoolFlag[INDESTRUCTIBLE_INDEX].value || dest->Variable[UNHOLYARMOR_INDEX].Value) { dest->CacheLock = 1; return; } // Calculate the costs to attack the unit. // Unit with the smallest attack costs will be taken. int cost = 0; int hp_damage_evaluate; if (Damage) { hp_damage_evaluate = CalculateDamage(*attacker, *dest, Damage); } else { hp_damage_evaluate = attacker->Stats->Variables[BASICDAMAGE_INDEX].Value + attacker->Stats->Variables[PIERCINGDAMAGE_INDEX].Value; } if (!player.IsEnemy(*dest)) { // a friend or neutral dest->CacheLock = 1; // Calc a negative cost // The gost is more important when the unit would be killed // by our fire. // It costs (is positive) if hp_damage_evaluate>dest->HP ...) // FIXME : assume that PRIORITY_FACTOR>HEALTH_FACTOR cost = HEALTH_FACTOR * (2 * hp_damage_evaluate - dest->Variable[HP_INDEX].Value) / (dtype.TileWidth * dtype.TileWidth); cost = std::max(cost, 1); cost = -cost; } else { // Priority 0-255 cost += dtype.DefaultStat.Variables[PRIORITY_INDEX].Value * PRIORITY_FACTOR; for (unsigned int i = 0; i < UnitTypeVar.GetNumberBoolFlag(); i++) { if (type.BoolFlag[i].AiPriorityTarget != CONDITION_TRUE) { if ((type.BoolFlag[i].AiPriorityTarget == CONDITION_ONLY) & (dtype.BoolFlag[i].value)) { cost -= AIPRIORITY_BONUS; } else if ((type.BoolFlag[i].AiPriorityTarget == CONDITION_FALSE) & (dtype.BoolFlag[i].value)) { cost += AIPRIORITY_BONUS; } } } // Remaining HP (Health) 0-65535 // Give a boost to unit we can kill in one shoot only // calculate HP which will remain in the enemy unit, after hit int effective_hp = (dest->Variable[HP_INDEX].Value - 2 * hp_damage_evaluate); // Unit we won't kill are evaluated the same // Unit we are sure to kill are all evaluated the same (except PRIORITY) clamp(&effective_hp, -hp_damage_evaluate, 0); // Here, effective_hp vary from -hp_damage_evaluate (unit will be killed) to 0 (unit can't be killed) // => we prefer killing rather than only hitting... cost += -effective_hp * HEALTH_FACTOR; // Unit can attack back. if (CanTarget(dtype, type)) { cost += CANATTACK_BONUS; } // the cost may be divided across multiple cells cost = cost / (dtype.TileWidth * dtype.TileWidth); cost = std::max(cost, 1); // Removed Unit's are in bunkers int d; if (attacker->Removed) { d = attacker->Container->MapDistanceTo(*dest); } else { d = attacker->MapDistanceTo(*dest); } int attackrange = attacker->Stats->Variables[ATTACKRANGE_INDEX].Max; if (d <= attackrange || (d <= range && UnitReachable(*attacker, *dest, attackrange))) { ++enemy_count; } else { dest->CacheLock = 1; } // Attack walls only if we are stuck in them if (dtype.BoolFlag[WALL_INDEX].value && d > 1) { dest->CacheLock = 1; } } // cost map is relative to attacker position const int x = dest->tilePos.x - attacker->tilePos.x + (size / 2); const int y = dest->tilePos.y - attacker->tilePos.y + (size / 2); Assert(x >= 0 && y >= 0); // Mark the good/bad array... for (int yy = 0; yy < dtype.TileHeight; ++yy) { for (int xx = 0; xx < dtype.TileWidth; ++xx) { int pos = (y + yy) * (size / 2) + (x + xx); if (pos >= good->size()) { printf("BUG: RangeTargetFinder::FillBadGood.Compute out of range. "\ "size: %d, pos: %d, " \ "x: %d, xx: %d, y: %d, yy: %d", size, pos, x, xx, y, yy); break; } if (cost < 0) { good->at(pos) -= cost; } else { bad->at(pos) += cost; } } } }
int CastMagic::FlyingWeapon(MonsterData* caster, int x, int y) { Monster * d = World.getDungeonManager().level(World.GetCurrentLevel()).getCell(x, y).GetMonster(); MonsterData* defender = World.getMonsterManager().FindMonsterData(d); if (caster == defender) { if (caster->isPlayer()) World.getTextManager().newLine("Not a good idea. "); return 0; } WorldBuilder & world = World; StandardMonsterActions::ShowTrajectory(caster->level, caster->pos.x, caster->pos.y, x, y, '*', Random::getInt(2,0) ? 255:128, 0, 255); if (defender == NULL || d == NULL) return 0; int attackStrength = caster->AttackStrength(); int defenceStrength = defender->DefendStrength(); attackStrength = attackStrength + (Random::getInt(7, 1) + Random::getInt(7, 1)); defenceStrength = defenceStrength + (Random::getInt(7, 1) + Random::getInt(7, 1)); if (attackStrength > defenceStrength) //caster hits defender { if (caster->isPlayer()) //player is caster { if (defender->AbsorbTest()) { World.getTextManager().newLine("The %s's armour deflected the magic missile. ", defender->monster.name.c_str()); } else { if (int effect = defender->monster.GetEffect(repelMissiles) > 0) //repel missiles { if (Random::getInt(5, 0) <= effect) { World.getTextManager().newLine("The %s repels your magic missile. ", defender->monster.name.c_str()); return 1; } } World.getTextManager().newLine("Your magic missile hits the %s. ", defender->monster.name.c_str()); defender->monster.stamina -= CalculateDamage(attackStrength, defenceStrength); if (defender->monster.stamina <= 0) { caster->experience += defender->monster.experience; caster->XP(); } } } else if (defender->isPlayer()) { if (defender->AbsorbTest()) { World.getTextManager().newLine("The %s's magic missile was deflected by your armour. ", caster->monster.name.c_str()); } else { if (int effect = defender->monster.GetEffect(repelMissiles) > 0) //repel missiles { if (Random::getInt(5, 0) <= effect) { World.getTextManager().newLine("The repel the %s's magic missile. ", caster->monster.name.c_str()); return 1; } } World.getTextManager().newLine("The %s's magic missile hits you. ", caster->monster.name.c_str()); defender->monster.stamina -= CalculateDamage(attackStrength, defenceStrength); World.getDeathMessage().SetDeathMessage("was killed by a %s's magic missile. ", caster->monster.name.c_str()); } } } else if (attackStrength < defenceStrength) //defender blocks caster { if (caster->isPlayer()) //player is caster { World.getTextManager().newLine("Your magic missile misses the %s. ", defender->monster.name.c_str()); } else if (defender->isPlayer()) //player is defender { World.getTextManager().newLine("You dodge the %s's magic missile. ", caster->monster.name.c_str()); } } else //no damage done { if (caster->isPlayer()) { World.getTextManager().newLine("The %s dodges your magic missile. ", defender->monster.name.c_str()); } if (defender->isPlayer()) { // if(defender->slots.weapon !=NULL) if (World.getMonsterManager().getMonsterItems().GetEquipment(defender, weapon)) { World.getTextManager().newLine("The %s's magic missile misses you. ", caster->monster.name.c_str()); } else { World.getTextManager().newLine("You dodge the %s's magic missile. ", caster->monster.name.c_str()); } } } return 1; }
/** ** Fire missile. ** ** @param unit Unit that fires the missile. */ void FireMissile(CUnit &unit, CUnit *goal, const Vec2i &goalPos) { Vec2i newgoalPos = goalPos; // Goal dead? if (goal) { Assert(!unit.Type->Missile.Missile->AlwaysFire || unit.Type->Missile.Missile->Range); if (goal->Destroyed) { DebugPrint("destroyed unit\n"); return; } if (goal->Removed) { return; } if (goal->CurrentAction() == UnitActionDie) { if (unit.Type->Missile.Missile->AlwaysFire) { newgoalPos = goal->tilePos; goal = NULL; } else { return; } } } // No missile hits immediately! //Wyrmgus start // if (unit.Type->Missile.Missile->Class == MissileClassNone) { if (unit.Type->Missile.Missile->Class == MissileClassNone || (unit.Type->Animations && unit.Type->Animations->Attack && unit.Type->Animations->RangedAttack && ((goal && unit.MapDistanceTo(*goal) <= 1) || (!goal && unit.MapDistanceTo(goalPos) <= 1)) && !unit.Container)) { // treat melee attacks from units that have both attack and ranged attack animations as having missile class none //Wyrmgus end //Wyrmgus start int damage = 0; //Wyrmgus end // No goal, take target coordinates if (!goal) { if (Map.WallOnMap(goalPos)) { //Wyrmgus start // if (Map.HumanWallOnMap(goalPos)) { if (Map.HumanWallOnMap(goalPos) && CalculateHit(unit, *UnitTypeHumanWall->Stats, NULL) == true) { //Wyrmgus end //Wyrmgus start PlayUnitSound(unit, VoiceHit); damage = CalculateDamageStats(unit, *UnitTypeHumanWall->Stats, NULL); //Wyrmgus end Map.HitWall(goalPos, //Wyrmgus start // CalculateDamageStats(*unit.Stats, // *UnitTypeHumanWall->Stats, unit.Variable[BLOODLUST_INDEX].Value)); damage); //Wyrmgus end //Wyrmgus start // } else { } else if (Map.OrcWallOnMap(goalPos) && CalculateHit(unit, *UnitTypeOrcWall->Stats, NULL) == true) { //Wyrmgus end //Wyrmgus start PlayUnitSound(unit, VoiceHit); damage = CalculateDamageStats(unit, *UnitTypeOrcWall->Stats, NULL); //Wyrmgus end Map.HitWall(goalPos, //Wyrmgus start // CalculateDamageStats(*unit.Stats, // *UnitTypeOrcWall->Stats, unit.Variable[BLOODLUST_INDEX].Value)); damage); //Wyrmgus end } return; } DebugPrint("Missile-none hits no unit, shouldn't happen!\n"); return; } //Wyrmgus start // HitUnit(&unit, *goal, CalculateDamage(unit, *goal, Damage)); if (CalculateHit(unit, *goal->Stats, goal) == true) { damage = CalculateDamage(unit, *goal, Damage); HitUnit(&unit, *goal, damage); PlayUnitSound(unit, VoiceHit); //apply Thorns damage if attacker is at melee range if (goal && goal->Variable[THORNSDAMAGE_INDEX].Value && unit.MapDistanceTo(*goal) <= 1) { int thorns_damage = std::max<int>(goal->Variable[THORNSDAMAGE_INDEX].Value - unit.Variable[ARMOR_INDEX].Value, 1); if (GameSettings.NoRandomness) { thorns_damage -= ((thorns_damage + 2) / 2) / 2; //if no randomness setting is used, then the damage will always return what would have been the average damage with randomness } else { thorns_damage -= SyncRand() % ((thorns_damage + 2) / 2); } HitUnit(goal, unit, thorns_damage); } } else { PlayUnitSound(unit, VoiceMiss); } //Wyrmgus end return; } // If Firing from inside a Bunker CUnit *from = GetFirstContainer(unit); const int dir = ((unit.Direction + NextDirection / 2) & 0xFF) / NextDirection; const PixelPos startPixelPos = Map.TilePosToMapPixelPos_Center(from->tilePos) + unit.Type->MissileOffsets[dir][0]; Vec2i dpos; if (goal) { Assert(goal->Type); // Target invalid? // Moved out of attack range? if (unit.MapDistanceTo(*goal) < unit.Type->MinAttackRange) { DebugPrint("Missile target too near %d,%d\n" _C_ unit.MapDistanceTo(*goal) _C_ unit.Type->MinAttackRange); // FIXME: do something other? return; } // Fire to nearest point of the unit! // If Firing from inside a Bunker if (unit.Container) { NearestOfUnit(*goal, GetFirstContainer(unit)->tilePos, &dpos); } else { dpos = goal->tilePos + goal->Type->GetHalfTileSize(); } } else { dpos = newgoalPos; // FIXME: Can this be too near?? } PixelPos destPixelPos = Map.TilePosToMapPixelPos_Center(dpos); Missile *missile = MakeMissile(*unit.Type->Missile.Missile, startPixelPos, destPixelPos); // // Damage of missile // if (goal) { missile->TargetUnit = goal; } missile->SourceUnit = &unit; }
/** ** Fire missile. ** ** @param unit Unit that fires the missile. */ void FireMissile(CUnit *unit) { int x; int y; int dx; int dy; CUnit *goal; Missile *missile; // // Goal dead? // goal = unit->Orders[0]->Goal; if (goal) { // Better let the caller/action handle this. if (goal->Destroyed) { DebugPrint("destroyed unit\n"); return; } if (goal->Removed || goal->Orders[0]->Action == UnitActionDie) { return; } // FIXME: Some missile hit the field of the target and all units on it. // FIXME: goal is already dead, but missile could hit others? } // // No missile hits immediately! // if (unit->Type->Missile.Missile->Class == MissileClassNone) { // No goal, take target coordinates if (!goal) { DebugPrint("Missile-none hits no unit, shouldn't happen!\n"); return; } HitUnit(unit, goal, CalculateDamage(unit, goal)); return; } // If Firing from inside a Bunker if (unit->Container) { x = unit->Container->X * TileSizeX + TileSizeX / 2; // missile starts in tile middle y = unit->Container->Y * TileSizeY + TileSizeY / 2; } else { x = unit->X * TileSizeX + TileSizeX / 2; // missile starts in tile middle y = unit->Y * TileSizeY + TileSizeY / 2; } if (goal) { Assert(goal->Type); // Target invalid? // // Moved out of attack range? // if (MapDistanceBetweenUnits(unit, goal) < unit->Type->MinAttackRange) { DebugPrint("Missile target too near %d,%d\n" _C_ MapDistanceBetweenUnits(unit, goal) _C_ unit->Type->MinAttackRange); // FIXME: do something other? return; } // Fire to nearest point of the unit! // If Firing from inside a Bunker if (unit->Container) { NearestOfUnit(goal, unit->Container->X, unit->Container->Y, &dx, &dy); } else { NearestOfUnit(goal, unit->X, unit->Y, &dx, &dy); } } else { dx = unit->Orders[0]->X; dy = unit->Orders[0]->Y; // FIXME: Can this be too near?? } // Fire to the tile center of the destination. dx = dx * TileSizeX + TileSizeX / 2; dy = dy * TileSizeY + TileSizeY / 2; missile = MakeMissile(unit->Type->Missile.Missile, x, y, dx, dy); // // Damage of missile // if (goal) { missile->TargetUnit = goal; goal->RefsIncrease(); } missile->SourceUnit = unit; unit->RefsIncrease(); }