/** ** Missile hits wall. ** ** @param missile Missile hitting the goal. ** @param tilePos Wall map tile position. ** @param splash Splash damage divisor. ** ** @todo FIXME: Support for more races. */ static void MissileHitsWall(const Missile &missile, const Vec2i &tilePos, int splash) { CUnitStats *stats; // stat of the wall. if (!Map.WallOnMap(tilePos)) { return; } if (missile.Damage) { // direct damage, spells mostly Map.HitWall(tilePos, missile.Damage / splash); return; } Assert(missile.SourceUnit != NULL); if (Map.HumanWallOnMap(tilePos)) { stats = UnitTypeHumanWall->Stats; } else { Assert(Map.OrcWallOnMap(tilePos)); stats = UnitTypeOrcWall->Stats; } //Wyrmgus start if (CalculateHit(*missile.SourceUnit, *stats, NULL) == 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 //Wyrmgus start // Map.HitWall(tilePos, CalculateDamageStats(*missile.SourceUnit->Stats, *stats, 0) / splash); Map.HitWall(tilePos, CalculateDamageStats(*missile.SourceUnit, *stats, NULL) / splash); //Wyrmgus end }
/** ** Calculate damage. ** ** @param attacker Attacker. ** @param goal Goal unit. ** ** @return damage produces on goal. */ static int CalculateDamage(const CUnit &attacker, const CUnit &goal) { if (!Damage) { // Use old method. return CalculateDamageStats(*attacker.Stats, *goal.Stats, attacker.Variable[BLOODLUST_INDEX].Value); } Assert(Damage); UpdateUnitVariables(const_cast<CUnit &>(attacker)); UpdateUnitVariables(const_cast<CUnit &>(goal)); TriggerData.Attacker = const_cast<CUnit *>(&attacker); TriggerData.Defender = const_cast<CUnit *>(&goal); const int res = EvalNumber(Damage); TriggerData.Attacker = NULL; TriggerData.Defender = NULL; return res; }
/** ** Calculate damage. ** ** @param attacker Attacker. ** @param goal Goal unit. ** @param formula Formula used to calculate damage. ** ** @return damage produces on goal. */ int CalculateDamage(const CUnit &attacker, const CUnit &goal, const NumberDesc *formula) { if (!formula) { // Use old method. //Wyrmgus start // return CalculateDamageStats(*attacker.Stats, *goal.Stats, // attacker.Variable[BLOODLUST_INDEX].Value); return CalculateDamageStats(attacker, *goal.Stats, &goal); //Wyrmgus end } Assert(formula); UpdateUnitVariables(const_cast<CUnit &>(attacker)); UpdateUnitVariables(const_cast<CUnit &>(goal)); TriggerData.Attacker = const_cast<CUnit *>(&attacker); TriggerData.Defender = const_cast<CUnit *>(&goal); const int res = EvalNumber(formula); TriggerData.Attacker = NULL; TriggerData.Defender = NULL; return res; }
/** ** Missile hits wall. ** ** @param missile Missile hitting the goal. ** @param tilePos Wall map tile position. ** @param splash Splash damage divisor. ** ** @todo FIXME: Support for more races. */ static void MissileHitsWall(const Missile &missile, const Vec2i &tilePos, int splash) { CUnitStats *stats; // stat of the wall. if (!Map.WallOnMap(tilePos)) { return; } if (missile.Damage) { // direct damage, spells mostly Map.HitWall(tilePos, missile.Damage / splash); return; } Assert(missile.SourceUnit != NULL); if (Map.HumanWallOnMap(tilePos)) { stats = UnitTypeHumanWall->Stats; } else { Assert(Map.OrcWallOnMap(tilePos)); stats = UnitTypeOrcWall->Stats; } Map.HitWall(tilePos, CalculateDamageStats(*missile.SourceUnit->Stats, *stats, 0) / splash); }
/** ** 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; }
/** ** 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; }