public bool ExecuteTurn() { lock (battleLock) { battleRandom.UpdateSeed(Round, Turn); ICombatList offensiveCombatList; ICombatList defensiveCombatList; BattleSide sideAttacking; // This will finalize any reports already started. BattleReport.CompleteReport(ReportState.Staying); #region Battle Start if (!BattleStarted) { // Makes sure battle is valid before even starting it // This shouldnt really happen but I remember it happening in the past // so until we can make sure that we dont even start invalid battles, it's better to leave it if (!IsBattleValid()) { BattleEnded(false); return(false); } BringWaitingTroopsIntoBattle(); BattleStarted = true; BattleReport.CreateBattleReport(); EnterBattle(this, Attackers, Defenders); } #endregion #region Targeting List <CombatList.Target> currentDefenders; ICombatGroup attackerGroup; ICombatObject attackerObject; do { #region Find Attacker var newRound = !battleOrder.NextObject(Round, Attackers, Defenders, out attackerObject, out attackerGroup, out sideAttacking); // Save the offensive/defensive side relative to the attacker to make it easier in this function to know // which side is attacking. defensiveCombatList = sideAttacking == BattleSide.Attack ? Defenders : Attackers; offensiveCombatList = sideAttacking == BattleSide.Attack ? Attackers : Defenders; if (newRound) { ++Round; Turn = 0; BringWaitingTroopsIntoBattle(); EnterRound(this, Attackers, Defenders, Round); // Since the EventEnterRound can remove the object from battle, we need to make sure he's still in the battle // If they aren't, then we just find a new attacker if (attackerObject != null && !offensiveCombatList.AllCombatObjects().Contains(attackerObject)) { continue; } } // Verify battle is still good to go if (attackerObject == null || !IsBattleValid()) { BattleEnded(true); return(false); } #endregion #region Find Target(s) var targetResult = defensiveCombatList.GetBestTargets(BattleId, attackerObject, out currentDefenders, battleFormulas.GetNumberOfHits(attackerObject, defensiveCombatList), Round); if (currentDefenders.Count == 0 || attackerObject.Stats.Atk == 0) { attackerObject.ParticipatedInRound(Round); dbManager.Save(attackerObject); SkippedAttacker(this, sideAttacking, attackerGroup, attackerObject); // If the attacker can't attack because it has no one in range, then we skip him and find another target right away. if (targetResult == CombatList.BestTargetResult.NoneInRange) { continue; } return(true); } #endregion break; }while (true); #endregion for (int attackIndex = 0; attackIndex < currentDefenders.Count; attackIndex++) { var defender = currentDefenders[attackIndex]; // Make sure the target is still in the battle (they can leave if someone else in the group took dmg and caused the entire group to leave) // We just skip incase they left while we were attacking // which means if the attacker is dealing splash they may deal less hits in this fringe case if (!defensiveCombatList.AllCombatObjects().Contains(defender.CombatObject)) { continue; } // Target is still in battle, attack it decimal carryOverDmg; AttackTarget(offensiveCombatList, defensiveCombatList, attackerGroup, attackerObject, sideAttacking, defender, attackIndex, Round, out carryOverDmg); // Add another target if we have to carry over some dmg and our current hit isnt from a carry over already if (carryOverDmg > 0m && !defender.DamageCarryOverPercentage.HasValue) { List <CombatList.Target> carryOverDefender; defensiveCombatList.GetBestTargets(BattleId, attackerObject, out carryOverDefender, 1, Round); if (carryOverDefender.Count > 0) { var target = carryOverDefender.First(); target.DamageCarryOverPercentage = carryOverDmg; currentDefenders.Insert(attackIndex + 1, target); } } } // Just a safety check if (attackerObject.Disposed) { throw new Exception("Attacker has been improperly disposed"); } attackerObject.ParticipatedInRound(Round); dbManager.Save(attackerObject); ExitTurn(this, Attackers, Defenders, Turn++); if (!IsBattleValid()) { BattleEnded(true); return(false); } // Remove any attackers that don't have anyone in range var groupsWithoutAnyoneInRange = Attackers.Where(attacker => attacker.All(combatObjects => !Defenders.HasInRange(combatObjects))).ToList(); foreach (var group in groupsWithoutAnyoneInRange) { Remove(group, BattleSide.Attack, ReportState.Exiting); } return(true); } }