protected virtual void AttackTarget(ICombatList offensiveCombatList, ICombatList defensiveCombatList, ICombatGroup attackerGroup, ICombatObject attacker, BattleSide sideAttacking, CombatList.Target target, int attackIndex, uint round, out decimal carryOverDmg) { var attackerCount = attacker.Count; var targetCount = target.CombatObject.Count; #region Damage decimal dmg = battleFormulas.GetAttackerDmgToDefender(attacker, target.CombatObject, round); if (target.DamageCarryOverPercentage.HasValue) { dmg *= target.DamageCarryOverPercentage.Value; } decimal actualDmg; target.CombatObject.CalcActualDmgToBeTaken(offensiveCombatList, defensiveCombatList, battleRandom, dmg, attackIndex, out actualDmg); Resource defenderDroppedLoot; int attackPoints; int initialCount = target.CombatObject.Count; carryOverDmg = 0; if (dmg > 0 && target.CombatObject.Hp - actualDmg < 0m) { carryOverDmg = (actualDmg - target.CombatObject.Hp) / actualDmg; } actualDmg = Math.Min(target.CombatObject.Hp, actualDmg); target.CombatObject.TakeDamage(actualDmg, out defenderDroppedLoot, out attackPoints); if (initialCount > target.CombatObject.Count) { UnitCountDecreased(this, sideAttacking == BattleSide.Attack ? BattleSide.Defense : BattleSide.Attack, target.Group, target.CombatObject, initialCount - target.CombatObject.Count); } attacker.DmgDealt += actualDmg; attacker.MaxDmgDealt = (ushort)Math.Max(attacker.MaxDmgDealt, actualDmg); attacker.MinDmgDealt = (ushort)Math.Min(attacker.MinDmgDealt, actualDmg); ++attacker.HitDealt; attacker.HitDealtByUnit += attacker.Count; target.CombatObject.DmgRecv += actualDmg; target.CombatObject.MaxDmgRecv = (ushort)Math.Max(target.CombatObject.MaxDmgRecv, actualDmg); target.CombatObject.MinDmgRecv = (ushort)Math.Min(target.CombatObject.MinDmgRecv, actualDmg); ++target.CombatObject.HitRecv; #endregion #region Loot and Attack Points // NOTE: In the following rewardStrategy calls we are in passing in whatever the existing implementations // of reward startegy need. If some specific implementation needs more info then add more params or change it to // take the entire BattleManager as a param. if (sideAttacking == BattleSide.Attack) { // Only give loot if we are attacking the first target in the list Resource loot; rewardStrategy.RemoveLoot(this, attackIndex, attacker, target.CombatObject, out loot); if (attackPoints > 0 || (loot != null && !loot.Empty)) { rewardStrategy.GiveAttackerRewards(attacker, attackPoints, loot ?? new Resource()); } } else { // Give defender rewards if there are any if (attackPoints > 0 || (defenderDroppedLoot != null && !defenderDroppedLoot.Empty)) { rewardStrategy.GiveDefendersRewards(attacker, attackPoints, defenderDroppedLoot ?? new Resource()); } } #endregion #region Object removal bool isDefenderDead = target.CombatObject.IsDead; ActionAttacked(this, sideAttacking, attackerGroup, attacker, target.Group, target.CombatObject, actualDmg, attackerCount, targetCount); if (isDefenderDead) { bool isGroupDead = target.Group.IsDead(); if (isGroupDead) { // Remove the entire group Remove(target.Group, sideAttacking == BattleSide.Attack ? BattleSide.Defense : BattleSide.Attack, ReportState.Dying); } else if (!target.CombatObject.Disposed) { // Only remove the single object BattleReport.WriteExitingObject(target.Group, sideAttacking != BattleSide.Attack, target.CombatObject); target.Group.Remove(target.CombatObject); } UnitKilled(this, sideAttacking == BattleSide.Attack ? BattleSide.Defense : BattleSide.Attack, target.Group, target.CombatObject); if (!target.CombatObject.Disposed) { target.CombatObject.ExitBattle(); } if (isGroupDead) { GroupKilled(this, target.Group); } } else { if (!target.CombatObject.Disposed) { dbManager.Save(target.CombatObject); } } #endregion }