/// <summary> /// Initializes a new instance of the RollDiceViewModel class. /// </summary> public CombatViewModel() { CombatRound round = new CombatRound(); if (IsInDesignMode) { // Code runs in Blend --> create design time data. for (int i = 0; i < 2; i++) { CombatForce combatForce = round.AddCombatForce("Combat Force " + i); for (int x = 0; x < 2; x++) { Unit unit = new Unit(); unit.CombatForce = combatForce; unit.Name = "Unit " + x; unit.TypeOfUnit = UnitType.Both; unit.Health = 6.0; unit.AttackPower = 6.0; combatForce.AddUnit(unit); } } } else { // Code runs "for real" ReloadUnitStats(); } RoundsOfCombat.Add(round); }
public void SimulateCombat(SimulatedCombatMode simulatedCombatMode) { Debug.WriteLine("Simulating Combat"); if (LastRound.CombatForces.Count <= 1) return; #region Preset Attacks to Skip foreach (var forces in LastRound.CombatForces) { if (forces.Attacking.Count == 0) forces.SkipAttack = false; } #endregion #region Write the Combat Summary if its the first thing we do if (RoundsOfCombat.Count == 1) { if (LastRound.RoundNumber == 0) { foreach (var force in LastRound.CombatForces) { LastRound.LogToSummary("[spoiler=" + force.Name + " Attacking With]", false); foreach (var unit in force.Units) { LastRound.LogToSummary(unit.ToString()); } LastRound.LogToSummary(); LastRound.LogToSummary("Total Units: " + force.TotalUnits); LastRound.LogToSummary("Total Health: " + force.TotalHealth); LastRound.LogToSummary("Total Attack: " + force.TotalAttack); LastRound.LogToSummary("[/spoiler]"); } } } #endregion CombatRound newRound = new CombatRound(); newRound.RoundNumber = LastRound.RoundNumber + 1; newRound.LogToSummary(string.Format("[spoiler=Round {0}]", newRound.RoundNumber)); #region Clone the Last Round's CombatForces and add them to our new Round List<CombatForce> ClonedCombatForces = new List<CombatForce>(); foreach (var cF in LastRound.CombatForces) { CombatForce combatForce = new CombatForce(cF); ClonedCombatForces.Add(combatForce); newRound.AddCombatForce(combatForce); } foreach (var clonedForce in ClonedCombatForces) { foreach (var cF in ClonedCombatForces) { // If they are the same, we skip if (clonedForce.Name == cF.Name) continue; // If they are not, we go into the last round and begin cloning its Attacking foreach (var oldForce in LastRound.CombatForces) { // We only want to clone if the Old Forces Name is equal to our cloned force if (oldForce.Name == clonedForce.Name) { // If it is, we get all the Attackings set for the old force foreach (var oldForceAttacking in oldForce.Attacking) { // And if the second loop through matches the attacking name, we add it if (oldForceAttacking.Name == cF.Name) { clonedForce.Attacking.Add(cF); break; } } } } } } #endregion #region Sort Forces into Easily accessible collections Dictionary<CombatForce, List<CombatForce>> attackerDefenderDictionary = new Dictionary<CombatForce, List<CombatForce>>(); foreach (var defender in ClonedCombatForces) { List<CombatForce> attackers = new List<CombatForce>(); foreach (var attacker in ClonedCombatForces) { if (defender == attacker) continue; foreach (var n in attacker.Attacking) { if (n.Name == defender.Name) attackers.Add(attacker); } } attackerDefenderDictionary.Add(defender, attackers); } #endregion Debug.WriteLine("Combat Forces Count {0}", ClonedCombatForces.Count); #region Check Electronic Warfare foreach (var attacker in ClonedCombatForces) { foreach (var unit in attacker.GetUnits("Electronic Warfare Ship")) { attacker.AttemptElectronicWarfare = true; break; } if (!attacker.AttemptElectronicWarfare) continue; Debug.WriteLine(attacker.Name + " Is Hacking"); newRound.LogToSummary(string.Format("[spoiler=Hack Roll "+attacker.Name+"]", false)); List<CombatForce> defenders = attackerDefenderDictionary[attacker]; List<Unit> combinedUnits = new List<Unit>(); foreach (var defender in defenders) { Debug.WriteLine("{0} Attempting Hack on {1}", attacker.Name, defender.Name); newRound.LogToSummary("Attempting to Hack: " + defender.Name); foreach (var unit in defender.Units) { combinedUnits.Add(unit); } } int hackRoll = Die.Roll(1, 100); double totalEffectedShips = (combinedUnits.Count * hackRoll) / 100; newRound.LogToSummary(string.Format("Rolled 1d100 : {0}, total {0}", hackRoll)); if (hackRoll < 25) { totalEffectedShips = 0.0; newRound.LogToSummary("Failed"); } else if (hackRoll < 50) { newRound.LogToSummary("Moderate Success"); Debug.WriteLine("Moderate Success, Hack Roll {0}, Total Units Effected {1}, Total Units {2}", hackRoll, totalEffectedShips, combinedUnits.Count); } else if (hackRoll < 75) { newRound.LogToSummary("Success"); Debug.WriteLine("Success, Hack Roll {0}, Total Units Effected {1}, Total Units {2}", hackRoll, totalEffectedShips, combinedUnits.Count); } else if (hackRoll <= 100) { newRound.LogToSummary("Great Success"); Debug.WriteLine("Great Success, Hack Roll {0}, Total Units Effected {1}, Total Units {2}", hackRoll, totalEffectedShips, combinedUnits.Count); } else { totalEffectedShips = 0.0; newRound.LogToSummary("Failed"); } if (totalEffectedShips > 0) newRound.LogToSummary(string.Format("{0} of Ships Effected by Electronic Warfare", totalEffectedShips)); newRound.LogToSummary("[br][b]Hack Result[/b]"); while (true) { if (totalEffectedShips <= 0.0) break; Unit unit = combinedUnits[Die.Roll(1, combinedUnits.Count) - 1]; if (unit.HackResult == ElectronicWarfareResult.None) { unit.HackResult = (ElectronicWarfareResult)(Die.Roll(1, (int)ElectronicWarfareResult.TakeDamage - 1)); totalEffectedShips -= 1.0; newRound.LogToSummary(string.Format("{0}: {1} Was Hacked. Result is {2}", unit.CombatForce.Name, unit.Name, StringHelpers.AddSpacesToSentence(unit.HackResult.ToString(), true))); } // To keep from an infinite loop, double check there is no more left to set bool escapeCheck = false; foreach (var u in combinedUnits) { if (u.HackResult == ElectronicWarfareResult.None) { escapeCheck = true; break; } } if (!escapeCheck) break; } newRound.LogToSummary("[/spoiler]", false); } #endregion #region Calculate Raw Damage Debug.WriteLine("Calculating Raw Damage"); foreach (var attacker in ClonedCombatForces) { if (attacker.SkipAttack) continue; List<CombatForce> defenderList = attackerDefenderDictionary[attacker]; if (defenderList.Count > 0) { newRound.LogToSummary("[spoiler=Attack Roll " + attacker.Name + "]", false); List<Unit> combinedDefenderUnits = new List<Unit>(); foreach (var defender in defenderList) { newRound.LogToSummary("Target: " + defender.Name); if (defender.IsInvulnerable) newRound.LogToSummary(defender.Name + " Is Invulnerable"); foreach (var unit in defender.Units) { combinedDefenderUnits.Add(unit); } } // Damage Tracker int damageDealt = 0; // Raw Damage int damageRoll = Die.Roll(1, (int)attacker.TotalAttack); if ((int)attacker.TotalAttack == 0) damageRoll = 0; damageDealt += damageRoll; newRound.LogToSummary(string.Format("Rolled 1d{0} : {1}, total {1}", (int)attacker.TotalAttack, damageRoll)); // Apply Admiral Command Scores damageDealt += attacker.CrewScore; newRound.LogToSummary(string.Format("+{0} Admiral/Command Score ({1})", attacker.CrewScore, attacker.Name)); #region Add 10 Damage for Hunter-Killers if its the first round and the unit wasn't detected if (newRound.RoundNumber == 1) { List<Unit> hunterKillers = attacker.GetUnits("Hunter-Killer"); foreach (var hunterKiller in hunterKillers) { newRound.LogToSummary("", true, true); newRound.LogToSummary(string.Format(hunterKiller.Name), true, true); foreach (var defender in defenderList) { if (defender.HasAdvancedScanners) { if (Die.Roll(1, 100) > 50) { newRound.LogToSummary(string.Format("Detected By Advanced Scanners {0}", defender.Name), true, true); hunterKiller.Detected = true; } } List<Unit> detectionCapableUnits = new List<Unit>(); foreach (var areaDefenceDestroyer in defender.GetUnits("Area Defence Destroyer")) detectionCapableUnits.Add(areaDefenceDestroyer); foreach (var corvette in defender.GetUnits("Corvette")) detectionCapableUnits.Add(corvette); foreach (var detectionCapableUnit in detectionCapableUnits) { if (Die.Roll(1, 100) > 50) { newRound.LogToSummary(string.Format("Detected By {0}: {1}", detectionCapableUnit.CombatForce.Name, detectionCapableUnit.Name), true, true); hunterKiller.Detected = true; } } } if (!hunterKiller.Detected) { newRound.LogToSummary(string.Format("+{0} : Not Detected Attack Bonus", 10), true, true); damageDealt += 10; } } } #endregion #region Attack Planet with SSBM Hunter-Killer if its the first round and the unit wasn't detected if (newRound.RoundNumber == 1) { List<Unit> hunterKillers = attacker.GetUnits("SSBM Hunter-Killer"); foreach (var ssbmHunterKiller in hunterKillers) { newRound.LogToSummary("", true, true); newRound.LogToSummary(string.Format(ssbmHunterKiller.Name), true, true); foreach (var defender in defenderList) { if (defender.HasAdvancedScanners) { if (Die.Roll(1, 100) > 50) { newRound.LogToSummary(string.Format("Detected By Advanced Scanners {0}", defender.Name), true, true); ssbmHunterKiller.Detected = true; } } List<Unit> detectionCapableUnits = new List<Unit>(); foreach (var areaDefenceDestroyer in defender.GetUnits("Area Defence Destroyer")) detectionCapableUnits.Add(areaDefenceDestroyer); foreach (var hunterKiller in defender.GetUnits("Hunter-Killer")) detectionCapableUnits.Add(hunterKiller); foreach (var detectionCapableUnit in detectionCapableUnits) { if (Die.Roll(1, 100) > 50) { newRound.LogToSummary(string.Format("Detected By {0}: {1}", detectionCapableUnit.CombatForce.Name, detectionCapableUnit.Name), true, true); ssbmHunterKiller.Detected = true; } } } if (!ssbmHunterKiller.Detected) { newRound.LogToSummary(string.Format("Not Detected. Important Asset Hit with Weapon of Mass Destruction"), true, true); } } } #endregion // Combine Units and check Negations int totalFighterNegations = 0; foreach (var unit in combinedDefenderUnits) { #region Take off 1d4 of damage for each Area Defence Destroyer if (unit.Name == "Area Defence Destroyer") { Debug.WriteLine("Activating Area Defence Destroyers"); int damageNegated = 0; damageNegated = Die.Roll(1, 5); damageDealt -= damageNegated; newRound.LogToSummary(string.Format("-{0} Area Defence Destroyer ({1}) : Rolled 1d5 : {0}, total {0}", damageNegated, unit.CombatForce.Name), true, true); } #endregion #region Take Damage if Electronic Warfare if (unit.HackResult == ElectronicWarfareResult.TakeDamage) { int hackDamage = Die.Roll(1, (int)(unit.Health / 2)); unit.Health -= hackDamage; newRound.LogToSummary(string.Format("+{1} Hack Damage: {0} took {1} points of Damage: Rolled 1d{2} : {1}, total {1}", unit.Name, hackDamage, (int)unit.Health)); } #endregion #region Total up Fighter Squadron Negations if (unit.Name == "Fighter Squadron") { totalFighterNegations += (int)unit.Health; } #endregion } if (totalFighterNegations > 0) { Debug.WriteLine("Negating Fighter Damage"); newRound.LogToSummary("[br][b]Fighter Result[/b]"); foreach (var attackingUnit in attacker.Units) { if (totalFighterNegations <= 0) break; if (attackingUnit.Name == "Fighter Squadron") { newRound.LogToSummary(string.Format("Fighter Squadron ({0} Units, {1}) was negated by Defenders Fighter Squadron ({2} Units)", (int)attackingUnit.Health, attackingUnit.CombatForce.Name, totalFighterNegations)); totalFighterNegations -= (int)attackingUnit.Health; } else if (attackingUnit.Name == "Strike Fighter Squadron" || attackingUnit.Name == "Gun Ship Squadron" || attackingUnit.Name == "Heavy Gunship Squadron") { int damageNegated = 0; for (int i = (int)attackingUnit.Health; i > 0; i--) { if (totalFighterNegations <= 0) break; damageNegated++; totalFighterNegations--; } newRound.LogToSummary(string.Format(attackingUnit.Name + " ({0} Units, {1}) was negated by Defenders Fighter Squadron ({2} Units) for {3} points of Damage", (int)attackingUnit.Health, attackingUnit.CombatForce.Name, totalFighterNegations, damageNegated)); damageDealt -= damageNegated; } } } newRound.LogToSummary("[br][b]Result[/b]"); int tempDamageDealt = damageDealt; while (tempDamageDealt > 0 ) { CombatForce force = defenderList[Die.Roll(1, defenderList.Count) - 1]; force.DamageDealt += 1.0; tempDamageDealt -= 1; } foreach (var force in defenderList) { double damageSummaryInput = 0; if (defenderList.Count > 1) damageSummaryInput = force.DamageDealt; else damageSummaryInput = damageDealt; newRound.LogToSummary(string.Format("{0}, Total Damage Dealt: {1}", force.Name, damageSummaryInput), true, true); } newRound.LogToSummary("[/spoiler]", false); } } #endregion #region Apply Damage Debug.WriteLine("Applying Damage"); foreach (var force in ClonedCombatForces) { newRound.LogToSummary("[spoiler=Combat Result " + force.Name+"]", false); newRound.LogToSummary(string.Format("Total Damage Dealt: {0}", force.DamageDealt)); while (force.DamageDealt > 0) { if (force.Units.Count <= 0) break; if (simulatedCombatMode == SimulatedCombatMode.RandomizedTargets) { double pointsOfDamage = 1.0; Unit unit = force.Units[Die.Roll(1, force.Units.Count) - 1]; if (unit.Invulnerable) { newRound.LogToSummary(string.Format("{0} is Invulnerable and shook off {1} points of Damage", unit.Name, pointsOfDamage)); } else { unit.Health -= 1.0; if (unit.Health <= 0) { force.RemoveUnit(unit); force.DestroyedUnits.Add(unit); newRound.LogToSummary(string.Format("[red]{0} took {1} points of Damage and was Destroyed[/red]", unit.Name, pointsOfDamage)); } else { newRound.LogToSummary(string.Format("[green]{0} took {1} points of Damage and has {2} HP Left[/green]", unit.Name, pointsOfDamage, unit.Health)); } } force.DamageDealt -= pointsOfDamage; } } newRound.LogToSummary("[br][b]Remaining Units[/b]"); foreach (var unit in force.Units) { unit.HackResult = ElectronicWarfareResult.None; newRound.LogToSummary(unit.ToString()); } newRound.LogToSummary(); newRound.LogToSummary("Total Units: " + force.TotalUnits); newRound.LogToSummary("Total Health: " + force.TotalHealth); newRound.LogToSummary("Total Attack: " + force.TotalAttack); newRound.LogToSummary("[/spoiler]", false); } #endregion #region Calculate Recoverable Forces foreach (var force in ClonedCombatForces) { if (force.Units.Count == 0) { int recoverablePercentageRoll = Die.Roll(1, 100); double recoverableUnitAmount = (force.DestroyedUnits.Count * recoverablePercentageRoll) / 100; newRound.LogToSummary("[spoiler=Units Recoverable " + force.Name + "]", false); newRound.LogToSummary(string.Format("Rolled 1d100 : {0}, total {0}", recoverablePercentageRoll)); newRound.LogToSummary(string.Format("{0}% of Units ({1} Units) are Recoverable", recoverablePercentageRoll, recoverableUnitAmount)); List<Unit> tempRecoverableUnits = new List<Unit>(); while (true) { if (recoverableUnitAmount < 1.0) break; if (force.DestroyedUnits.Count <= 0) break; Unit unit = force.DestroyedUnits[Die.Roll(1, force.DestroyedUnits.Count) - 1]; tempRecoverableUnits.Add(unit); force.DestroyedUnits.Remove(unit); recoverableUnitAmount -= 1.0; } newRound.LogToSummary("[hr][b]Recoverable Units[/b]"); foreach (var unit in tempRecoverableUnits) { newRound.LogToSummary(string.Format("{0}", unit.ToString())); force.DestroyedUnits.Add(unit); } tempRecoverableUnits.Clear(); newRound.LogToSummary("[/spoiler]", false); } } #endregion // Reset Values on the Forces foreach (var force in newRound.CombatForces) { force.DamageDealt = 0.0; //force.IsInvulnerable = false; //force.AttemptElectronicWarfare = false; //force.SkipAttack = false; } // Lastly, Lock the Last Round, finish the Summary then add the new Round LastRound.RoundLocked = true; newRound.LogToSummary(string.Format("[/spoiler]")); RoundsOfCombat.Add(newRound); }