void CombatCalculation(Unit attacker, Unit defender) { // diplomacy relation change diplomacyHandler.ModifyRelation(defender.GetOwner().GetPlayerId(), attacker.GetOwner().GetPlayerId(), -4); Node currentNode = attacker.GetCurrentNode(); // this is completely arbitrary but it seems to give good results float attackerStatDiff = 1f + (attacker.GetAttack() - (int)(0.60 * defender.GetDefence())) / 60; float defenderStatDiff = 1f + (defender.GetAttack() - (int)(0.60 * attacker.GetDefence())) / 60; int attackerRoll = (int)(0.12 * Random.Range((int)(attacker.GetSize() * 0.6), attacker.GetSize()) * attackerStatDiff); int defenderRoll = (int)(0.12 * Random.Range((int)(defender.GetSize() * 0.6), defender.GetSize()) * defenderStatDiff); Officer attackerCenter = attacker.GetCenterOfficer(); Officer attackerFlank1 = attacker.GetFlank1Officer(); Officer attackerFlank2 = attacker.GetFlank2Officer(); Officer defenderCenter = defender.GetCenterOfficer(); Officer defenderFlank1 = defender.GetFlank1Officer(); Officer defenderFlank2 = defender.GetFlank2Officer(); // store damage rolls for battle report int aCAttack = 0; int dCAttack = 0; int aCDefence = 0; int dCDefence = 0; int aF1Attack = 0; int dF1Attack = 0; int aF1Defence = 0; int dF1Defence = 0; int aF2Attack = 0; int dF2Attack = 0; int aF2Defence = 0; int dF2Defence = 0; string aCTactic = "None"; string dCTactic = "None"; string aF1Tactic = "None"; string aF2Tactic = "None"; string dF1Tactic = "None"; string dF2Tactic = "None"; float aCAttackModifier = 1f; float aCDefenceModifier = 1f; float dCAttackModifier = 1f; float dCDefenceModifier = 1f; float aF1AttackModifier = 1f; float aF1DefenceModifier = 1f; float dF1AttackModifier = 1f; float dF1DefenceModifier = 1f; float aF2AttackModifier = 1f; float aF2DefenceModifier = 1f; float dF2AttackModifier = 1f; float dF2DefenceModifier = 1f; // // choose tactic // if (attackerCenter != null) { aCTactic = ChooseCombatTactic(attackerCenter); aCAttackModifier = GetTacticAttackBonus(aCTactic); aCDefenceModifier = GetTacticDefenceBonus(aCTactic); } if (defenderCenter != null) { dCTactic = ChooseCombatTactic(defenderCenter); dCAttackModifier = GetTacticAttackBonus(dCTactic); dCDefenceModifier = GetTacticDefenceBonus(dCTactic); } if (attackerFlank1 != null) { aF1Tactic = ChooseCombatTactic(attackerFlank1); aF1AttackModifier = GetTacticAttackBonus(aF1Tactic); aF1DefenceModifier = GetTacticDefenceBonus(aF1Tactic); } if (attackerFlank2 != null) { aF2Tactic = ChooseCombatTactic(attackerFlank2); aF2AttackModifier = GetTacticAttackBonus(aF2Tactic); aF2DefenceModifier = GetTacticDefenceBonus(aF2Tactic); } if (defenderFlank1 != null) { dF1Tactic = ChooseCombatTactic(defenderFlank1); dF1AttackModifier = GetTacticAttackBonus(dF1Tactic); dF1DefenceModifier = GetTacticDefenceBonus(dF1Tactic); } if (defenderFlank2 != null) { dF2Tactic = ChooseCombatTactic(defenderFlank2); dF2AttackModifier = GetTacticAttackBonus(dF2Tactic); dF2DefenceModifier = GetTacticDefenceBonus(dF2Tactic); } if (attackerCenter != null) { aCAttack = (int)(attackerCenter.GetPow() * Random.Range(2, 4) * attackerStatDiff * aCAttackModifier); int finalAttack = aCAttack; attackerCenter.AddXp("pow", 10 + (int)(attackerCenter.GetPow() * 0.1)); if (defenderCenter != null) { dCDefence = (int)(defenderCenter.GetFri() * Random.Range(1, 3) * defenderStatDiff * dCDefenceModifier); finalAttack -= dCDefence; defenderCenter.AddXp("fri", 10 + (int)(defenderCenter.GetFri() * 0.1)); } if (finalAttack < 0) { finalAttack = 0; } attackerRoll += finalAttack; attackerCenter.ProgressFriendship(attackerFlank1, 5); attackerCenter.ProgressFriendship(attackerFlank2, 5); } if (attackerFlank1 != null) { aF1Attack = (int)(attackerFlank1.GetPow() * Random.Range(2, 4) * attackerStatDiff * aF1AttackModifier); int finalAttack = aF1Attack; attackerFlank1.AddXp("pow", 10 + (int)(attackerFlank1.GetPow() * 0.1)); if (defenderFlank2 != null) { dF2Defence = (int)(defenderFlank2.GetFri() * Random.Range(1, 3) * defenderStatDiff * dF2DefenceModifier); finalAttack -= dF2Defence; defenderFlank2.AddXp("fri", 10 + (int)(defenderFlank2.GetFri() * 0.1)); } if (finalAttack < 0) { finalAttack = 0; } attackerRoll += finalAttack; attackerFlank1.ProgressFriendship(attackerCenter, 5); attackerFlank1.ProgressFriendship(attackerFlank2, 5); } if (attackerFlank2 != null) { aF2Attack = (int)(attackerFlank2.GetPow() * Random.Range(2, 4) * attackerStatDiff * aF2AttackModifier); int finalAttack = aF1Attack; attackerFlank2.AddXp("pow", 10 + (int)(attackerFlank2.GetPow() * 0.1)); if (defenderFlank1 != null) { dF1Defence = (int)(defenderFlank1.GetFri() * Random.Range(1, 3) * defenderStatDiff * dF1DefenceModifier); finalAttack -= dF1Defence; defenderFlank1.AddXp("fri", 10 + (int)(defenderFlank1.GetFri() * 0.1)); } if (finalAttack < 0) { finalAttack = 0; } attackerRoll += finalAttack; attackerFlank2.ProgressFriendship(attackerCenter, 5); attackerFlank2.ProgressFriendship(attackerFlank1, 5); } if (defenderCenter != null) { dCAttack = (int)(defenderCenter.GetPow() * Random.Range(2, 4) * defenderStatDiff * dCAttackModifier); int finalAttack = dCAttack; defenderCenter.AddXp("pow", 10 + (int)(defenderCenter.GetPow() * 0.1)); if (attackerCenter != null) { aCDefence = (int)(attackerCenter.GetFri() * Random.Range(1, 3) * attackerStatDiff * aCDefenceModifier); finalAttack -= aCDefence; attackerCenter.AddXp("fri", 10 + (int)(attackerCenter.GetFri() * 0.1)); } if (finalAttack < 0) { finalAttack = 0; } defenderRoll += finalAttack; defenderCenter.ProgressFriendship(defenderFlank1, 5); defenderCenter.ProgressFriendship(defenderFlank2, 5); } if (defenderFlank1 != null) { dF1Attack = (int)(defenderFlank1.GetPow() * Random.Range(2, 4) * defenderStatDiff * dF1AttackModifier); int finalAttack = dF1Attack; defenderFlank1.AddXp("pow", 10 + (int)(defenderFlank1.GetPow() * 0.1)); if (attackerFlank2 != null) { aF2Defence = (int)(attackerFlank2.GetFri() * Random.Range(1, 3) * attackerStatDiff * aF2DefenceModifier); finalAttack -= aF2Defence; attackerFlank2.AddXp("fri", 10 + (int)(attackerFlank2.GetFri() * 0.1)); } if (finalAttack < 0) { finalAttack = 0; } defenderRoll += finalAttack; defenderFlank1.ProgressFriendship(defenderCenter, 5); defenderFlank1.ProgressFriendship(defenderFlank2, 5); } if (defenderFlank2 != null) { dF2Attack = (int)(defenderFlank2.GetPow() * Random.Range(2, 4) * defenderStatDiff * dF2AttackModifier); int finalAttack = dF2Attack; defenderFlank2.AddXp("pow", 10 + (int)(defenderFlank2.GetPow() * 0.1)); if (attackerFlank1 != null) { aF1Defence = (int)(attackerFlank1.GetFri() * Random.Range(1, 2) * attackerStatDiff * aF1DefenceModifier); finalAttack -= aF1Defence; attackerFlank1.AddXp("fri", 10 + (int)(attackerFlank1.GetFri() * 0.1)); } if (finalAttack < 0) { finalAttack = 0; } defenderRoll += finalAttack; defenderFlank2.ProgressFriendship(defenderCenter, 5); defenderFlank2.ProgressFriendship(defenderFlank1, 5); } float attackerFortificationBonus = 1; float defenderFortificationBonus = 1; if (attacker.GetOwner() == currentNode.GetOwner()) { attackerFortificationBonus = 1 - (currentNode.GetFortificationsLevel() / 10); } else if (defender.GetOwner() == currentNode.GetOwner()) { defenderFortificationBonus = 1 - (currentNode.GetFortificationsLevel() / 10); } attackerRoll = (int)(attackerRoll * defenderFortificationBonus); defenderRoll = (int)(defenderRoll * attackerFortificationBonus); if (attackerRoll > defender.GetSize()) { attackerRoll = defender.GetSize(); } if (defenderRoll > attacker.GetSize()) { defenderRoll = attacker.GetSize(); } if (attackerRoll < 0) { attackerRoll = 0; } if (defenderRoll < 0) { defenderRoll = 0; } if (defender != null) { defender.ModifySize((int)(-attackerRoll)); defender.SetActionPoints(defender.GetActionPoints() - 2); // unit spends 2 action points for fighting } if (attacker != null) { attacker.ModifySize((int)(-defenderRoll)); attacker.SetActionPoints(attacker.GetActionPoints() - 2); // unit spends 2 action points for fighting } /// officer loyalty change and capture chance // should turn this into a loop if (attacker.GetSize() == 0) { if (attackerCenter != null) { attackerCenter.ModifyLoyalty(-15); if (attackerCenter.GetLoyalty() < 25) { int captureRoll = Random.Range(0, 2); if (captureRoll == 0) { defender.GetOwner().AddOfficer(attackerCenter); attacker.GetOwner().RemoveOfficer(attackerCenter); attackerCenter.ModifyLoyalty(15); if (defender.GetOwner().isHuman) { uiHandler.OfficerGained(attackerCenter); } if (attacker.GetOwner().isHuman) { string logMessage = $"{attackerCenter.GetName()} was captured and persuaded to join {defender.GetOwner().GetFactionLeader().GetName()}!"; uiHandler.LogMessage(logMessage, defender.GetOwner().GetPlayerColor()); } } } } if (attackerFlank1 != null) { attackerFlank1.ModifyLoyalty(-15); if (attackerFlank1.GetLoyalty() < 25) { int captureRoll = Random.Range(0, 2); if (captureRoll == 0) { defender.GetOwner().AddOfficer(attackerFlank1); attacker.GetOwner().RemoveOfficer(attackerFlank1); attackerFlank1.ModifyLoyalty(15); if (defender.GetOwner().isHuman) { uiHandler.OfficerGained(attackerFlank1); } if (attacker.GetOwner().isHuman) { string logMessage = $"{attackerFlank1.GetName()} was captured and persuaded to join {defender.GetOwner().GetFactionLeader().GetName()}!"; uiHandler.LogMessage(logMessage, defender.GetOwner().GetPlayerColor()); } } } } if (attackerFlank2 != null) { attackerFlank2.ModifyLoyalty(-15); if (attackerFlank2.GetLoyalty() < 25) { int captureRoll = Random.Range(0, 2); if (captureRoll == 0) { defender.GetOwner().AddOfficer(attackerFlank2); attacker.GetOwner().RemoveOfficer(attackerFlank2); attackerFlank2.ModifyLoyalty(15); if (defender.GetOwner().isHuman) { uiHandler.OfficerGained(attackerFlank2); } if (attacker.GetOwner().isHuman) { string logMessage = $"{attackerFlank2.GetName()} was captured and persuaded to join {defender.GetOwner().GetFactionLeader().GetName()}!"; uiHandler.LogMessage(logMessage, defender.GetOwner().GetPlayerColor()); } } } } } if (defender.GetSize() == 0) { if (defenderCenter != null) { defenderCenter.ModifyLoyalty(-15); if (defenderCenter.GetLoyalty() < 25) { int captureRoll = Random.Range(0, 2); if (captureRoll == 0) { attacker.GetOwner().AddOfficer(defenderCenter); defender.GetOwner().RemoveOfficer(defenderCenter); defenderCenter.ModifyLoyalty(15); if (attacker.GetOwner().isHuman) { uiHandler.OfficerGained(defenderCenter); } if (defender.GetOwner().isHuman) { string logMessage = $"{defenderCenter.GetName()} was captured and persuaded to join {attacker.GetOwner().GetFactionLeader().GetName()}!"; uiHandler.LogMessage(logMessage, attacker.GetOwner().GetPlayerColor()); } } } } if (defenderFlank1 != null) { defenderFlank1.ModifyLoyalty(-15); if (defenderFlank1.GetLoyalty() < 25) { int captureRoll = Random.Range(0, 2); if (captureRoll == 0) { attacker.GetOwner().AddOfficer(defenderFlank1); defender.GetOwner().RemoveOfficer(defenderFlank1); defenderFlank1.ModifyLoyalty(15); if (attacker.GetOwner().isHuman) { uiHandler.OfficerGained(defenderFlank1); } if (defender.GetOwner().isHuman) { string logMessage = $"{defenderFlank1.GetName()} was captured and persuaded to join {attacker.GetOwner().GetFactionLeader().GetName()}!"; uiHandler.LogMessage(logMessage, attacker.GetOwner().GetPlayerColor()); } } } } if (defenderFlank2 != null) { defenderFlank2.ModifyLoyalty(-15); if (defenderFlank2.GetLoyalty() < 25) { int captureRoll = Random.Range(0, 2); if (captureRoll == 0) { attacker.GetOwner().AddOfficer(defenderFlank2); defender.GetOwner().RemoveOfficer(defenderFlank2); defenderFlank2.ModifyLoyalty(15); if (attacker.GetOwner().isHuman) { uiHandler.OfficerGained(defenderFlank2); } if (defender.GetOwner().isHuman) { string logMessage = $"{defenderFlank2.GetName()} was captured and persuaded to join {attacker.GetOwner().GetFactionLeader().GetName()}!"; uiHandler.LogMessage(logMessage, attacker.GetOwner().GetPlayerColor()); } } } } } // battle report for human player if (attacker.GetOwner().isHuman || defender.GetOwner().isHuman) { uiHandler.ShowBattleReport(attacker, defender, attackerRoll, defenderRoll, aCAttack, aCDefence, aF1Attack, aF1Defence, aF2Attack, aF2Defence, dCAttack, dCDefence, dF1Attack, dF1Defence, dF2Attack, dF2Defence, aCTactic, dCTactic, aF1Tactic, dF1Tactic, aF2Tactic, dF2Tactic ); mainCamera.transform.position = new Vector3(currentNode.transform.position.x, currentNode.transform.position.y, mainCamera.transform.position.z); requirePlayerInput = true; } } string ChooseCombatTactic(Officer officer) { List availableTactics = new List(); foreach (string trait in officer.GetTraits()) { switch (trait) { case "Brave": availableTactics.Add("Charge"); availableTactics.Add("First Strike"); break; case "Calm": availableTactics.Add("Adaptive"); availableTactics.Add("Skirmish"); break; case "Siren": availableTactics.Add("Alluring Song"); availableTactics.Add("Piercing Screech"); availableTactics.Add("Alluring Song"); availableTactics.Add("Piercing Screech"); break; case "Princess of the Moon": availableTactics.Add("Nightmare"); availableTactics.Add("Nightmare"); break; case "Princess of the Sun": availableTactics.Add("To The Moon"); availableTactics.Add("To The Moon"); break; case "Princess of Love": availableTactics.Add("Crystal Shield"); availableTactics.Add("Crystal Shield"); break; case "Princess of Friendship": availableTactics.Add("Friendship Laser"); availableTactics.Add("Friendship Laser"); break; case "Great and Powerful": availableTactics.Add("Smokescreen"); availableTactics.Add("Disrupt"); availableTactics.Add("Gamble Cast"); break; case "God of Chaos": availableTactics.Add("Discord"); availableTactics.Add("Disrupt"); availableTactics.Add("Timewarp"); availableTactics.Add("Gamble Cast"); break; case "Yak": availableTactics.Add("Smash"); availableTactics.Add("Crush"); availableTactics.Add("Smash"); availableTactics.Add("Crush"); break; case "Dragon": availableTactics.Add("Scorch"); availableTactics.Add("Smokescreen"); break; case "Alicorn": availableTactics.Add("Magical Barrier"); availableTactics.Add("Alicorn Magic"); break; case "Kirin": availableTactics.Add("Bamboo Dance"); break; case "Changeling": availableTactics.Add("Deceive"); availableTactics.Add("Infiltrate"); break; case "Adept Commander": availableTactics.Add("Feint"); availableTactics.Add("Taunt"); availableTactics.Add("Raid"); availableTactics.Add("Shieldwall"); break; case "Expert Commander": availableTactics.Add("Fire Attack"); availableTactics.Add("Pincer"); availableTactics.Add("Unyielding"); break; case "Adept Mage": availableTactics.Add("Protective Bubble"); availableTactics.Add("Magical Artillery"); availableTactics.Add("Enchanted Weapons"); break; case "Expert Mage": availableTactics.Add("Teleportation"); availableTactics.Add("Elemental Beam"); availableTactics.Add("Firestorm"); availableTactics.Add("Earthquake"); break; case "Charged": availableTactics.Add("Lightning Strike"); break; case "Musician": availableTactics.Add("Inspire"); break; case "Trickster": availableTactics.Add("Decoy"); availableTactics.Add("Feint"); if (officer.magic > 80) { availableTactics.Add("Gamble Cast"); } break; case "Queen of the Changelings": availableTactics.Add("Overwhelm"); break; case "Timelord": availableTactics.Add("Timewarp"); break; case "Cutie Mark Crusader": availableTactics.Add("Rousing Speech"); availableTactics.Add("Lead By Example"); break; case "Father of Monsters": availableTactics.Add("Grogar's Bell"); availableTactics.Add("Grogar's Bell"); break; case "Mystic": availableTactics.Add("Eldritch Horror"); break; case "Cheerleader": availableTactics.Add("Inspire"); availableTactics.Add("Dance"); break; case "Inspiring Leader": availableTactics.Add("Rousing Speech"); availableTactics.Add("First Strike"); availableTactics.Add("Maintain Order"); break; case "Examplary Leader": availableTactics.Add("Rousing Speech"); availableTactics.Add("Hold The Line"); availableTactics.Add("Lead By Example"); break; } } if (availableTactics.Count == 0) { availableTactics.Add("Aggressive"); availableTactics.Add("Defensive"); } return availableTactics[Random.Range(0, availableTactics.Count)]; } float GetTacticAttackBonus(string tactic) { switch (tactic) { case "Aggressive": return 1.05f; case "Defensive": return 0.95f; case "Charge": return 1.2f; case "Adaptive": return 1.05f; case "Alluring Song": return 1.05f; case "Piercing Screech": return 1.2f; case "Nightmare": return 1.7f; case "To The Moon": return 1.7f; case "Crystal Shield": return 1f; case "Friendship Laser": return 1.7f; case "Smokescreen": return 0.7f; case "Disrupt": return 1.2f; case "Discord": return 1.5f; case "Timewarp": return 1.5f; case "Smash": return 1.2f; case "Crush": return 1.3f; case "Scorch": return 1.2f; case "Magical Barrier": return 1f; case "Alicorn Magic": return 1.4f; case "Bamboo Dance": return 1f; case "Deceive": return 1.15f; case "Infiltrate": return 1.25f; case "Feint": return 0.8f; case "Taunt": return 1.35f; case "Raid": return 1.3f; case "Shieldwall": return 0.8f; case "Fire Attack": return 1.6f; case "Pincer": return 1.5f; case "Unyielding": return 1f; case "Protective Bubble": return 0.95f; case "Magical Artillery": return 1.1f; case "Enchanted Weapons": return 1.15f; case "Teleportation": return 1.25f; case "Elemental Beam": return 1.4f; case "Firestorm": return 1.4f; case "Earthquake": return 1.4f; case "Lightning Strike": return 1.4f; case "Inspire": return 1.1f; case "Decoy": return 1.1f; case "Overwhelm": return 1.7f; case "Grogar's Bell": return 2f; case "Eldritch Horror": return 1.5f; case "Dance": return 1.15f; case "Lead By Example": return 1.15f; case "First Strike": return 1.15f; case "Rousing Speech": return 1.1f; case "Maintain Order": return 0.8f; case "Hold The Line": return 1.1f; case "Gamble Cast": return Random.Range(0.2f, 2f); default: return 1f; } } float GetTacticDefenceBonus(string tactic) { switch (tactic) { case "Aggressive": return 0.95f; case "Defensive": return 1.05f; case "Charge": return 0.9f; case "Adaptive": return 1.05f; case "Alluring Song": return 1.2f; case "Piercing Screech": return 1.05f; case "Nightmare": return 1.2f; case "To The Moon": return 1.2f; case "Crystal Shield": return 2.5f; case "Friendship Laser": return 1.2f; case "Smokescreen": return 1.7f; case "Disrupt": return 1f; case "Discord": return 1.2f; case "Timewarp": return 1.5f; case "Smash": return 1f; case "Crush": return 0.9f; case "Scorch": return 1f; case "Magical Barrier": return 2f; case "Alicorn Magic": return 1.1f; case "Bamboo Dance": return 1.5f; case "Deceive": return 1.15f; case "Infiltrate": return 1f; case "Feint": return 1.2f; case "Taunt": return 0.85f; case "Raid": return 1f; case "Shieldwall": return 1.6f; case "Fire Attack": return 0.7f; case "Pincer": return 1f; case "Unyielding": return 1.8f; case "Protective Bubble": return 1.8f; case "Magical Artillery": return 1.3f; case "Enchanted Weapons": return 1.15f; case "Teleportation": return 1.25f; case "Elemental Beam": return 1.2f; case "Firestorm": return 1.2f; case "Earthquake": return 1.2f; case "Lightning Strike": return 1.2f; case "Inspire": return 1.1f; case "Decoy": return 1.15f; case "Overwhelm": return 1.2f; case "Grogar's Bell": return 1.5f; case "Eldritch Horror": return 1f; case "Dance": return 1.15f; case "Lead By Example": return 1.15f; case "First Strike": return 1f; case "Rousing Speech": return 1.10f; case "Maintain Order": return 1.4f; case "Hold The Line": return 1.6f; case "Skirmish": return 1.1f; case "Gamble Cast": return Random.Range(0.2f, 2f); default: return 1f; } }