/// <summary> /// Implements the processes involved in the pillage of a fief by an army /// </summary> /// <param name="a">The pillaging army</param> /// <param name="f">The fief being pillaged</param> public static ProtoMessage PillageFief(Army a, Fief f) { ProtoMessage result = new ProtoMessage(); bool pillageCancelled = false; bool bailiffPresent = false; Army fiefArmy = null; // check if bailiff present in fief (he'll lead the army) if (f.bailiff != null) { for (int i = 0; i < f.charactersInFief.Count; i++) { if (f.charactersInFief[i] == f.bailiff) { bailiffPresent = true; break; } } } // if bailiff is present, create an army and attempt to give battle // no bailiff = no leader = pillage is unopposed by defending forces if (bailiffPresent) { // create temporary army for battle fiefArmy = f.CreateDefendingArmy(); // give battle and get result ProtoBattle battleResults; pillageCancelled = Battle.GiveBattle(fiefArmy, a, out battleResults, circumstance: "pillage"); if (pillageCancelled) { string toDisplay = "The pillaging force has been forced to retreat by the fief's defenders!"; result.ResponseType = DisplayMessages.PillageRetreat; // Let owner know that pillage attempt has been thwarted Globals_Game.UpdatePlayer(f.owner.playerID, DisplayMessages.PillageRetreat); return(result); } else { // check still have enough days left if (a.days < 7) { // Inform fief owner pillage attempt thwarted Globals_Game.UpdatePlayer(f.owner.playerID, DisplayMessages.PillageDays); result.ResponseType = DisplayMessages.PillageDays; pillageCancelled = true; return(result); } } } if (!pillageCancelled) { // process pillage return(Pillage_Siege.ProcessPillage(f, a)); } result.ResponseType = DisplayMessages.Success; result.Message = "The pillage was successful"; return(result); }
/// <summary> /// Implements the processes involved in a battle between two armies in the field /// </summary> /// <returns>bool indicating whether attacking army is victorious</returns> /// <remarks> /// Predicate: assumes attacker has sufficient days /// Predicate: assumes attacker has leader /// Predicate: assumes attacker in same fief as defender /// Predicate: assumes defender not besieged in keep /// Predicate: assumes attacker and defender not same army /// </remarks> /// <param name="attacker">The attacking army</param> /// <param name="defender">The defending army</param> /// <param name="circumstance">string indicating circumstance of battle</param> public static bool GiveBattle(Army attacker, Army defender, out ProtoBattle battleResults, string circumstance = "battle") { Contract.Requires(attacker != null && defender != null && circumstance != null); battleResults = new ProtoBattle(); bool attackerVictorious = false; bool battleHasCommenced = false; bool attackerLeaderDead = false; bool defenderLeaderDead = false; // check if losing army has disbanded bool attackerDisbanded = false; bool defenderDisbanded = false; bool siegeRaised = false; uint[] battleValues = new uint[2]; double[] casualtyModifiers = new double[2]; double statureChange = 0; // if applicable, get siege Siege thisSiege = null; string thisSiegeID = defender.CheckIfBesieger(); if (!String.IsNullOrWhiteSpace(thisSiegeID)) { // get siege thisSiege = Globals_Game.siegeMasterList[thisSiegeID]; } // get starting troop numbers uint attackerStartTroops = attacker.CalcArmySize(); uint defenderStartTroops = defender.CalcArmySize(); uint attackerCasualties = 0; uint defenderCasualties = 0; // get leaders Character attackerLeader = attacker.GetLeader(); Character defenderLeader = defender.GetLeader(); // if(attackerLeader!=null) { battleResults.attackerLeader = attackerLeader.firstName + " " + attackerLeader.familyName; // } // if(defenderLeader!=null) { battleResults.defenderLeader = defenderLeader.firstName + " " + defenderLeader.familyName; // } battleResults.attackerOwner = attacker.GetOwner().firstName + " " + attacker.GetOwner().familyName; battleResults.defenderOwner = defender.GetOwner().firstName + " " + defender.GetOwner().familyName; battleResults.battleLocation = attacker.GetLocation().id; // introductory text for message switch (circumstance) { case "pillage": battleResults.circumstance = 1; break; case "siege": battleResults.circumstance = 2; break; default: battleResults.circumstance = 0; break; } // get battle values for both armies battleValues = attacker.CalculateBattleValues(defender); // check if attacker has managed to bring defender to battle // case 1: defending army sallies during siege to attack besieger = battle always occurs if (circumstance.Equals("siege")) { battleHasCommenced = true; } // case 2: defending militia attacks pillaging army during pollage = battle always occurs else if (circumstance.Equals("pillage")) { battleHasCommenced = true; } // case 3: defender aggression and combatOdds allows battle else if (defender.aggression != 0) { if (defender.aggression == 1) { // get odds int battleOdds = Battle.GetBattleOdds(attacker, defender); // if odds OK, give battle if (battleOdds <= defender.combatOdds) { battleHasCommenced = true; } // if not, check for battle else { battleHasCommenced = Battle.BringToBattle(battleValues[0], battleValues[1], circumstance); if (!battleHasCommenced) { defender.ProcessRetreat(1); } } } else { battleHasCommenced = true; } } // otherwise, check to see if the attacker can bring the defender to battle else { battleHasCommenced = Battle.BringToBattle(battleValues[0], battleValues[1], circumstance); if (!battleHasCommenced) { defender.ProcessRetreat(1); } } battleResults.battleTookPlace = battleHasCommenced; if (battleHasCommenced) { List <string> disbandedArmies = new List <string>(); List <string> retreatedArmies = new List <string>(); List <string> deadCharacters = new List <string>(); // WHO HAS WON? // calculate if attacker has won attackerVictorious = Battle.DecideBattleVictory(battleValues[0], battleValues[1]); // UPDATE STATURE if (attackerVictorious) { statureChange = 0.8 * (defender.CalcArmySize() / Convert.ToDouble(10000)); battleResults.statureChangeAttacker = statureChange; attacker.GetOwner().AdjustStatureModifier(statureChange); statureChange = -0.5 * (attacker.CalcArmySize() / Convert.ToDouble(10000)); battleResults.statureChangeDefender = statureChange; defender.GetOwner().AdjustStatureModifier(statureChange); } else { statureChange = 0.8 * (attacker.CalcArmySize() / Convert.ToDouble(10000)); battleResults.statureChangeDefender = statureChange; defender.GetOwner().AdjustStatureModifier(statureChange); statureChange = -0.5 * (defender.CalcArmySize() / Convert.ToDouble(10000)); battleResults.statureChangeAttacker = statureChange; attacker.GetOwner().AdjustStatureModifier(statureChange); } // CASUALTIES // calculate troop casualties for both sides casualtyModifiers = Battle.CalculateBattleCasualties(attackerStartTroops, defenderStartTroops, battleValues[0], battleValues[1], attackerVictorious); uint totalAttackTroopsLost = 0; uint totalDefendTroopsLost = 0; // if losing side sustains >= 50% casualties, disbands if (attackerVictorious) { // either indicate losing army to be disbanded if (casualtyModifiers[1] >= 0.5) { defenderDisbanded = true; disbandedArmies.Add(defender.owner); totalDefendTroopsLost = defender.CalcArmySize(); } // OR apply troop casualties to losing army else { totalDefendTroopsLost = defender.ApplyTroopLosses(casualtyModifiers[1]); } // apply troop casualties to winning army totalAttackTroopsLost = attacker.ApplyTroopLosses(casualtyModifiers[0]); } else { if (casualtyModifiers[0] >= 0.5) { attackerDisbanded = true; disbandedArmies.Add(attacker.owner); totalAttackTroopsLost = attacker.CalcArmySize(); } else { totalAttackTroopsLost = attacker.ApplyTroopLosses(casualtyModifiers[0]); } totalDefendTroopsLost = defender.ApplyTroopLosses(casualtyModifiers[1]); } battleResults.attackerCasualties = totalAttackTroopsLost; battleResults.defenderCasualties = totalDefendTroopsLost; // UPDATE TOTAL SIEGE LOSSES, if appropriate // NOTE: the defender in this battle is the attacker in the siege and v.v. if (thisSiege != null) { // update total siege attacker (defender in this battle) losses thisSiege.totalCasualtiesAttacker += Convert.ToInt32(totalDefendTroopsLost); // update total siege defender (attacker in this battle) losses if (circumstance.Equals("siege")) { thisSiege.totalCasualtiesDefender += Convert.ToInt32(totalAttackTroopsLost); } } // get casualty figures (for message) if (!attackerDisbanded) { // get attacker casualties attackerCasualties = totalAttackTroopsLost; } if (!defenderDisbanded) { // get defender casualties defenderCasualties = totalDefendTroopsLost; } // DAYS // adjust days // NOTE: don't adjust days if is a siege (will be deducted elsewhere) if (!circumstance.Equals("siege")) { if (attackerLeader != null) { attackerLeader.AdjustDays(1); } // need to check for defender having no leader if (defenderLeader != null) { defenderLeader.AdjustDays(1); } else { defender.days -= 1; } } // RETREATS // create array of armies (for easy processing) Army[] bothSides = { attacker, defender }; // check if either army needs to retreat int[] retreatDistances = Battle.CheckForRetreat(attacker, defender, casualtyModifiers[0], casualtyModifiers[1], attackerVictorious); // if is pillage or siege, attacking army (the fief's army) doesn't retreat // if is pillage, the defending army (the pillagers) always retreats if has lost if (circumstance.Equals("pillage") || circumstance.Equals("siege")) { retreatDistances[0] = 0; } if (circumstance.Equals("pillage")) { if (attackerVictorious) { retreatDistances[1] = 1; } } // if have retreated, perform it for (int i = 0; i < retreatDistances.Length; i++) { if (retreatDistances[i] > 0) { bothSides[i].ProcessRetreat(retreatDistances[i]); } } // If attacker has retreated add to retreat list if (retreatDistances[0] > 0) { retreatedArmies.Add(battleResults.attackerOwner); } // If defender retreated add to retreat list if (retreatDistances[1] > 0) { retreatedArmies.Add(battleResults.defenderOwner); } // PC/NPC INJURIES/DEATHS // check if any PCs/NPCs have been wounded or killed bool characterDead = false; // 1. ATTACKER uint friendlyBV = battleValues[0]; uint enemyBV = battleValues[1]; // if army leader a PC, check entourage if (attackerLeader is PlayerCharacter) { for (int i = 0; i < (attackerLeader as PlayerCharacter).myNPCs.Count; i++) { if ((attackerLeader as PlayerCharacter).myNPCs[i].inEntourage) { characterDead = (attackerLeader as PlayerCharacter).myNPCs[i].CalculateCombatInjury(casualtyModifiers[0]); } // process death, if applicable if (characterDead) { (attackerLeader as PlayerCharacter).myNPCs[i].ProcessDeath("injury"); } } } // check army leader if (attackerLeader != null) { attackerLeaderDead = attackerLeader.CalculateCombatInjury(casualtyModifiers[0]); } // process death, if applicable if (attackerLeaderDead) { deadCharacters.Add(attackerLeader.firstName + " " + attackerLeader.familyName); Character newLeader = null; // if is pillage, do NOT elect new leader for attacking army if (!circumstance.Equals("pillage")) { // if possible, elect new leader from entourage if (attackerLeader is PlayerCharacter) { if ((attackerLeader as PlayerCharacter).myNPCs.Count > 0) { // get new leader newLeader = (attackerLeader as PlayerCharacter).ElectNewArmyLeader(); } } // assign newLeader (can assign null leader if none found) attacker.AssignNewLeader(newLeader); } } else { // if pillage, if fief's army loses, make sure bailiff always returns to keep if (circumstance.Equals("pillage")) { if (!attackerVictorious) { attackerLeader.inKeep = true; } } } // 2. DEFENDER // need to check if defending army had a leader if (defenderLeader != null) { // if army leader a PC, check entourage if (defenderLeader is PlayerCharacter) { for (int i = 0; i < (defenderLeader as PlayerCharacter).myNPCs.Count; i++) { if ((defenderLeader as PlayerCharacter).myNPCs[i].inEntourage) { characterDead = (defenderLeader as PlayerCharacter).myNPCs[i].CalculateCombatInjury(casualtyModifiers[1]); } // process death, if applicable if (characterDead) { (defenderLeader as PlayerCharacter).myNPCs[i].ProcessDeath("injury"); } } } // check army leader defenderLeaderDead = defenderLeader.CalculateCombatInjury(casualtyModifiers[1]); // process death, if applicable if (defenderLeaderDead) { deadCharacters.Add(defenderLeader.firstName + " " + defenderLeader.familyName); Character newLeader = null; // if possible, elect new leader from entourage if (defenderLeader is PlayerCharacter) { if ((defenderLeader as PlayerCharacter).myNPCs.Count > 0) { // get new leader newLeader = (defenderLeader as PlayerCharacter).ElectNewArmyLeader(); } } // assign newLeader (can assign null leader if none found) defender.AssignNewLeader(newLeader); } } battleResults.deaths = deadCharacters.ToArray(); battleResults.retreatedArmies = retreatedArmies.ToArray(); battleResults.attackerVictorious = attackerVictorious; // check for SIEGE RELIEF if (thisSiege != null) { battleResults.isSiege = true; battleResults.siegeBesieger = thisSiege.GetBesiegingPlayer().firstName + " " + thisSiege.GetBesiegingPlayer().familyName; battleResults.siegeDefender = thisSiege.GetDefendingPlayer().firstName + " " + thisSiege.GetDefendingPlayer().familyName; // attacker (relieving army) victory or defender (besieging army) retreat = relief if ((attackerVictorious) || (retreatDistances[1] > 0)) { // indicate siege raised siegeRaised = true; battleResults.siegeRaised = true; } // check to see if siege raised due to death of siege owner with no heir else if ((defenderLeaderDead) && ((defenderLeader as PlayerCharacter) == thisSiege.GetBesiegingPlayer())) { // get siege owner's heir Character thisHeir = (defenderLeader as PlayerCharacter).GetHeir(); if (thisHeir == null) { battleResults.DefenderDeadNoHeir = true; // indicate siege raised siegeRaised = true; } } } } // =================== construct and send JOURNAL ENTRY // ID uint entryID = Globals_Game.GetNextJournalEntryID(); // personae // personae tags vary depending on circumstance string attackOwnTag = "|attackerOwner"; string attackLeadTag = "|attackerLeader"; string defendOwnTag = "|defenderOwner"; string defendLeadTag = "|defenderLeader"; if ((circumstance.Equals("pillage")) || (circumstance.Equals("siege"))) { attackOwnTag = "|sallyOwner"; attackLeadTag = "|sallyLeader"; defendOwnTag = "|defenderAgainstSallyOwner"; defendLeadTag = "|defenderAgainstSallyLeader"; } List <string> tempPersonae = new List <string>(); tempPersonae.Add(defender.GetOwner().charID + defendOwnTag); if (attackerLeader != null) { tempPersonae.Add(attackerLeader.charID + attackLeadTag); } if (defenderLeader != null) { tempPersonae.Add(defenderLeader.charID + defendLeadTag); } tempPersonae.Add(attacker.GetOwner().charID + attackOwnTag); tempPersonae.Add(attacker.GetLocation().owner.charID + "|fiefOwner"); if ((!circumstance.Equals("pillage")) && (!circumstance.Equals("siege"))) { tempPersonae.Add("all|all"); } string[] battlePersonae = tempPersonae.ToArray(); // location string battleLocation = attacker.GetLocation().id; // put together new journal entry JournalEntry battleResult = new JournalEntry(entryID, Globals_Game.clock.currentYear, Globals_Game.clock.currentSeason, battlePersonae, "battle", battleResults, loc: battleLocation); // add new journal entry to pastEvents Globals_Game.AddPastEvent(battleResult); // display pop-up informational message battleResults.ActionType = Actions.Update; battleResults.ResponseType = DisplayMessages.BattleResults; if (battleHasCommenced) { Globals_Game.UpdatePlayer(defender.GetOwner().playerID, DisplayMessages.BattleBringSuccess, new string[] { battleResults.attackerOwner }); } else { Globals_Game.UpdatePlayer(defender.GetOwner().playerID, DisplayMessages.BattleBringFail, new string[] { battleResults.attackerOwner }); } // end siege if appropriate if (siegeRaised) { //HACK thisSiege.SiegeEnd(false, DisplayMessages.BattleResults, new string[] { DisplaySiegeResults(battleResults) }); thisSiege = null; // ensure if siege raised correct value returned to Form1.siegeReductionRound method if (circumstance.Equals("siege")) { attackerVictorious = true; } } // process leader deaths if (defenderLeaderDead) { defenderLeader.ProcessDeath("injury"); } else if (attackerLeaderDead) { attackerLeader.ProcessDeath("injury"); } // DISBANDMENT // if is pillage, attacking (temporary) army always disbands after battle if (circumstance.Equals("pillage")) { attackerDisbanded = true; } // process army disbandings (after all other functions completed) if (attackerDisbanded) { attacker.DisbandArmy(); attacker = null; } if (defenderDisbanded) { defender.DisbandArmy(); defender = null; } return(attackerVictorious); }
/// <summary> /// Respond to ransom demands /// </summary> /// <param name="paid">Whether or not ransom is to be paid</param> /// <returns>Bool indicating success</returns> public bool RansomResponse(bool paid, out ProtoMessage error) { error = null; // Check if type is ransom if (this.type.Equals("ransom")) { // Check if already replied if (replied) { // Already replied error = new ProtoMessage(); error.ResponseType = DisplayMessages.RansomRepliedAlready; return(false); } Character captive = null; PlayerCharacter captor; PlayerCharacter captiveHeadOfHousehold; // Confirm captive is still alive and being held foreach (string persona in personae) { string[] split = persona.Split(new char[] { '|' }); if (split[1].Equals("Captive")) { captive = Globals_Game.getCharFromID(split[0]); } } if (captive == null) { // Captive does not exist- error error = new ProtoMessage(); error.ResponseType = DisplayMessages.ErrorGenericCharacterUnidentified; Globals_Server.logError("Captive unidentified in JEntry: " + this.jEntryID); return(false); } else if (!captive.isAlive) { // Captive is dead error = new ProtoMessage(); error.ResponseType = DisplayMessages.RansomCaptiveDead; return(false); } captor = Globals_Game.getCharFromID(captive.captorID) as PlayerCharacter; if (captor == null) { // Captive does not have a captor error = new ProtoMessage(); error.ResponseType = DisplayMessages.NotCaptive; return(false); } captiveHeadOfHousehold = captive.GetPlayerCharacter(); if (captiveHeadOfHousehold == null) { // Captive is not an employee, family member or player character Globals_Server.logError("Captive has no PlayerCharacter: " + captive.charID); error = new ProtoMessage(); error.ResponseType = DisplayMessages.ErrorGenericCharacterUnidentified; return(false); } if (paid) { // Get ransom amount uint ransom = 0; if (!UInt32.TryParse(entryDetails.MessageFields[1], out ransom)) { // Error parsing to int Globals_Server.logError("Could not parse ransom to uint in JEntry: " + jEntryID); error = new ProtoMessage(); error.ResponseType = DisplayMessages.ErrorGenericMessageInvalid; return(false); } else { // Check captive's head of household has the funds to release if (captiveHeadOfHousehold.GetHomeFief().GetAvailableTreasury(false) >= ransom) { if (!captiveHeadOfHousehold.GetHomeFief().TreasuryTransfer(captor.GetHomeFief(), (Int32)ransom, out error)) { return(false); } else { // Release captive captor.ReleaseCaptive(captive); replied = true; Globals_Game.UpdatePlayer(captor.playerID, DisplayMessages.RansomPaid, new string[] { captive.firstName + " " + captive.familyName }); return(true); } } else { // Insufficient funds error = new ProtoMessage(); error.ResponseType = DisplayMessages.ErrorGenericInsufficientFunds; return(false); } } } // If not paying ransom, inform captor else { // Create journal entry and update captor string[] newPersonae = new string[] { captive.charID + "|Captive", captor.charID + "|Captor", captiveHeadOfHousehold.charID + "|HeadOfCaptiveFamily" }; ProtoMessage deniedMessage = new ProtoMessage(); deniedMessage.ResponseType = DisplayMessages.RansonDenied; deniedMessage.MessageFields = new string[] { captive.firstName + " " + captive.familyName, captor.firstName + " " + captor.familyName, captiveHeadOfHousehold.firstName + " " + captiveHeadOfHousehold.familyName }; Globals_Game.UpdatePlayer(captor.playerID, deniedMessage); JournalEntry ransomDenied = new JournalEntry(Globals_Game.GetNextJournalEntryID(), Globals_Game.clock.currentYear, Globals_Game.clock.currentSeason, newPersonae, "ransomDenied", deniedMessage); Globals_Game.AddPastEvent(ransomDenied); replied = true; return(true); } } else { // Not a ransom error = new ProtoMessage(); error.ResponseType = DisplayMessages.EntryNotRansom; return(false); } }