/// <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> /// Processes the actions involved with a marriage /// </summary> /// <returns>bool indicating whether engagement was processed successfully</returns> public bool ProcessMarriage() { bool success = false; // get interested parties PlayerCharacter headOfFamilyBride = null; PlayerCharacter headOfFamilyGroom = null; Character bride = null; Character groom = null; for (int i = 0; i < this.personae.Length; i++) { string thisPersonae = this.personae[i]; string[] thisPersonaeSplit = thisPersonae.Split('|'); switch (thisPersonaeSplit[1]) { case "headOfFamilyGroom": headOfFamilyGroom = Globals_Game.pcMasterList[thisPersonaeSplit[0]]; break; case "headOfFamilyBride": headOfFamilyBride = Globals_Game.pcMasterList[thisPersonaeSplit[0]]; break; case "bride": bride = Globals_Game.npcMasterList[thisPersonaeSplit[0]]; break; case "groom": if (Globals_Game.pcMasterList.ContainsKey(thisPersonaeSplit[0])) { groom = Globals_Game.pcMasterList[thisPersonaeSplit[0]]; } else if (Globals_Game.npcMasterList.ContainsKey(thisPersonaeSplit[0])) { groom = Globals_Game.npcMasterList[thisPersonaeSplit[0]]; } break; default: break; } } // ID uint marriageID = Globals_Game.GetNextJournalEntryID(); // date uint year = Globals_Game.clock.currentYear; byte season = Globals_Game.clock.currentSeason; // personae string headOfFamilyBrideEntry = headOfFamilyBride.charID + "|headOfFamilyBride"; string headOfFamilyGroomEntry = headOfFamilyGroom.charID + "|headOfFamilyGroom"; string thisBrideEntry = bride.charID + "|bride"; string thisGroomEntry = groom.charID + "|groom"; string allEntry = "all|all"; string[] marriagePersonae = new string[] { headOfFamilyGroomEntry, headOfFamilyBrideEntry, thisBrideEntry, thisGroomEntry, allEntry }; // type string type = "marriage"; string[] fields = new string[3]; fields[0] = groom.firstName + " " + groom.familyName; fields[1] = bride.firstName + " " + groom.familyName; fields[2] = bride.familyName; // description ProtoMessage marriage = new ProtoMessage(); marriage.MessageFields = fields; marriage.ResponseType = DisplayMessages.JournalMarriage; // create and add a marriage entry to the pastEvents journal JournalEntry marriageEntry = new JournalEntry(marriageID, year, season, marriagePersonae, type, marriage, null); success = Globals_Game.AddPastEvent(marriageEntry); if (success) { // remove fiancees bride.fiancee = null; groom.fiancee = null; // add spouses bride.spouse = groom.charID; groom.spouse = bride.charID; // change wife's family bride.familyID = groom.familyID; bride.familyName = groom.familyName; // switch myNPCs headOfFamilyBride.myNPCs.Remove(bride as NonPlayerCharacter); headOfFamilyGroom.myNPCs.Add(bride as NonPlayerCharacter); // move wife to groom's location bride.location = groom.location; // check to see if headOfFamilyBride should receive increase in stature // get highest rank for headOfFamilyBride and headOfFamilyGroom Rank brideHighestRank = headOfFamilyBride.GetHighestRank(); Rank groomHighestRank = headOfFamilyGroom.GetHighestRank(); // compare ranks if ((brideHighestRank != null) && (groomHighestRank != null)) { if (groomHighestRank.id < brideHighestRank.id) { headOfFamilyBride.AdjustStatureModifier((brideHighestRank.id - groomHighestRank.id) * 0.4); } } } return(success); }
/// <summary> /// Inserts the supplied PlayerCharacter's ID into the Position's officeHolder variable /// </summary> /// <param name="newPositionHolder">PlayerCharacter being assigned to the Position</param> public void BestowPosition(PlayerCharacter newPositionHolder) { PlayerCharacter oldPositionHolder = null; // remove existing holder if necessary if (!String.IsNullOrWhiteSpace(this.officeHolder)) { // get current holder if (Globals_Game.pcMasterList.ContainsKey(this.officeHolder)) { oldPositionHolder = Globals_Game.pcMasterList[this.officeHolder]; } // remove from position this.RemoveFromOffice(oldPositionHolder); } // assign position this.officeHolder = newPositionHolder.charID; // update stature newPositionHolder.AdjustStatureModifier(this.stature); // CREATE JOURNAL ENTRY // get interested parties bool success = true; PlayerCharacter king = this.GetKingdom().owner; // ID uint entryID = Globals_Game.GetNextJournalEntryID(); // date uint year = Globals_Game.clock.currentYear; byte season = Globals_Game.clock.currentSeason; // personae List <string> tempPersonae = new List <string>(); tempPersonae.Add("all|all"); tempPersonae.Add(king.charID + "|king"); tempPersonae.Add(newPositionHolder.charID + "|newPositionHolder"); if (oldPositionHolder != null) { tempPersonae.Add(oldPositionHolder.charID + "|oldPositionHolder"); } string[] thisPersonae = tempPersonae.ToArray(); // type string type = "grantPosition"; // description String[] fields = new string[] { this.title[0].name, king.firstName + " " + king.familyName, newPositionHolder.firstName + " " + newPositionHolder.familyName, "" }; if (oldPositionHolder != null) { fields[3] = "; This has necessitated the removal of " + oldPositionHolder.firstName + " " + oldPositionHolder.familyName + " from the position"; } ProtoMessage bestowPosition = new ProtoMessage(); bestowPosition.MessageFields = fields; bestowPosition.ResponseType = DisplayMessages.RankTitleTransfer; // create and add a journal entry to the pastEvents journal JournalEntry thisEntry = new JournalEntry(entryID, year, season, thisPersonae, type, bestowPosition); success = Globals_Game.AddPastEvent(thisEntry); }
/// <summary> /// Processes the actions involved with an engagement /// </summary> /// <returns>bool indicating whether engagement was processed successfully</returns> public bool ProcessEngagement() { bool success = false; // get interested parties PlayerCharacter headOfFamilyBride = null; PlayerCharacter headOfFamilyGroom = null; Character bride = null; Character groom = null; for (int i = 0; i < this.personae.Length; i++) { string thisPersonae = this.personae[i]; string[] thisPersonaeSplit = thisPersonae.Split('|'); switch (thisPersonaeSplit[1]) { case "headOfFamilyBride": headOfFamilyBride = Globals_Game.pcMasterList[thisPersonaeSplit[0]]; break; case "headOfFamilyGroom": headOfFamilyGroom = Globals_Game.pcMasterList[thisPersonaeSplit[0]]; break; case "bride": bride = Globals_Game.npcMasterList[thisPersonaeSplit[0]]; break; case "groom": if (Globals_Game.pcMasterList.ContainsKey(thisPersonaeSplit[0])) { groom = Globals_Game.pcMasterList[thisPersonaeSplit[0]]; } else if (Globals_Game.npcMasterList.ContainsKey(thisPersonaeSplit[0])) { groom = Globals_Game.npcMasterList[thisPersonaeSplit[0]]; } break; default: break; } } // ID uint replyID = Globals_Game.GetNextJournalEntryID(); // date uint year = Globals_Game.clock.currentYear; byte season = Globals_Game.clock.currentSeason; if (season == 3) { season = 0; year++; } else { season++; } // personae string headOfFamilyBrideEntry = headOfFamilyBride.charID + "|headOfFamilyBride"; string headOfFamilyGroomEntry = headOfFamilyGroom.charID + "|headOfFamilyGroom"; string thisBrideEntry = bride.charID + "|bride"; string thisGroomEntry = groom.charID + "|groom"; string[] marriagePersonae = new string[] { headOfFamilyGroomEntry, headOfFamilyBrideEntry, thisBrideEntry, thisGroomEntry }; // type string type = "marriage"; // create and add a marriage entry to the scheduledEvents journal JournalEntry marriageEntry = new JournalEntry(replyID, year, season, marriagePersonae, type, null); success = Globals_Game.AddScheduledEvent(marriageEntry); // show bride and groom as engaged if (success) { bride.fiancee = groom.charID; groom.fiancee = bride.charID; } return(success); }
// TODO I suspect there may be issues with this if any of the characters die. Test. /// <summary> /// Allows a character to reply to a marriage proposal /// </summary> /// <returns>bool indicating whether reply was processed successfully</returns> /// <param name="proposalAccepted">bool indicating whether proposal accepted</param> public bool ReplyToProposal(bool proposalAccepted) { bool success = true; string[] replyFields = new string[4]; // get interested parties PlayerCharacter headOfFamilyBride = null; PlayerCharacter headOfFamilyGroom = null; Character bride = null; Character groom = null; for (int i = 0; i < this.personae.Length; i++) { string thisPersonae = this.personae[i]; string[] thisPersonaeSplit = thisPersonae.Split('|'); switch (thisPersonaeSplit[1]) { case "headOfFamilyBride": headOfFamilyBride = Globals_Game.pcMasterList[thisPersonaeSplit[0]]; break; case "headOfFamilyGroom": headOfFamilyGroom = Globals_Game.pcMasterList[thisPersonaeSplit[0]]; break; case "bride": bride = Globals_Game.npcMasterList[thisPersonaeSplit[0]]; break; case "groom": if (Globals_Game.pcMasterList.ContainsKey(thisPersonaeSplit[0])) { groom = Globals_Game.pcMasterList[thisPersonaeSplit[0]]; } else if (Globals_Game.npcMasterList.ContainsKey(thisPersonaeSplit[0])) { groom = Globals_Game.npcMasterList[thisPersonaeSplit[0]]; } break; default: break; } } // ID uint replyID = Globals_Game.GetNextJournalEntryID(); // date uint year = Globals_Game.clock.currentYear; byte season = Globals_Game.clock.currentSeason; // personae List <string> tempPersonae = new List <string>(); tempPersonae.Add(headOfFamilyBride.charID + "|headOfFamilyBride"); tempPersonae.Add(headOfFamilyGroom.charID + "|headOfFamilyGroom"); tempPersonae.Add(bride.charID + "|bride"); tempPersonae.Add(groom.charID + "|groom"); if (proposalAccepted) { tempPersonae.Add("all|all"); } string[] myReplyPersonae = tempPersonae.ToArray(); // type string type = ""; if (proposalAccepted) { type = "proposalAccepted"; } else { type = "proposalRejected"; } // description replyFields[0] = groom.firstName + " " + groom.familyName; replyFields[1] = bride.firstName + " " + bride.familyName; if (proposalAccepted) { replyFields[2] = "ACCEPTED"; } else { replyFields[2] = "REJECTED"; } replyFields[3] = headOfFamilyBride.firstName + " " + headOfFamilyBride.familyName; ProtoMessage proposalReply = new ProtoMessage(); proposalReply.MessageFields = replyFields; proposalReply.ResponseType = DisplayMessages.JournalProposalReply; // create and send a proposal reply (journal entry) JournalEntry myProposalReply = new JournalEntry(replyID, year, season, myReplyPersonae, type, proposalReply, null); success = Globals_Game.AddPastEvent(myProposalReply); if (success) { string[] newFields = new string[this.entryDetails.MessageFields.Length + 2]; Array.Copy(this.entryDetails.MessageFields, newFields, this.entryDetails.MessageFields.Length); newFields[newFields.Length - 1] = Globals_Game.clock.seasons[season] + ", " + year; this.entryDetails.MessageFields = newFields; this.replied = true; // mark proposal as replied if (proposalAccepted) { this.entryDetails.MessageFields[this.entryDetails.MessageFields.Length - 2] = "ACCEPTED"; } else { this.entryDetails.MessageFields[this.entryDetails.MessageFields.Length - 2] = "REJECTED"; } // if accepted, process engagement if (proposalAccepted) { myProposalReply.ProcessEngagement(); } } return(success); }
/// <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); } }
/// <summary> /// Allows an attacking army to lay siege to an enemy fief /// </summary> /// <param name="attacker">The attacking army</param> /// <param name="target">The fief to be besieged</param> public static Siege SiegeStart(Army attacker, Fief target) { Army defenderGarrison = null; Army defenderAdditional = null; // check for existence of army in keep for (int i = 0; i < target.armies.Count; i++) { // get army Army armyInFief = Globals_Game.armyMasterList[target.armies[i]]; // check is in keep Character armyLeader = armyInFief.GetLeader(); if (armyLeader != null) { if (armyLeader.inKeep) { // check owner is same as that of fief (i.e. can help in siege) if (armyInFief.GetOwner() == target.owner) { defenderAdditional = armyInFief; break; } } } } // create defending force defenderGarrison = target.CreateDefendingArmy(); // get the minumum days of all army objects involved double minDays = Math.Min(attacker.days, defenderGarrison.days); if (defenderAdditional != null) { minDays = Math.Min(minDays, defenderAdditional.days); } // get defenderAdditional ID, or null if no defenderAdditional string defAddID = null; if (defenderAdditional != null) { defAddID = defenderAdditional.armyID; } // create siege object Siege mySiege = new Siege(Globals_Game.GetNextSiegeID(), Globals_Game.clock.currentYear, Globals_Game.clock.currentSeason, attacker.GetOwner().charID, target.owner.charID, attacker.armyID, defenderGarrison.armyID, target.id, minDays, target.keepLevel, defAdd: defAddID); // add to master list Globals_Game.siegeMasterList.Add(mySiege.siegeID, mySiege); // add to siege owners mySiege.GetBesiegingPlayer().mySieges.Add(mySiege.siegeID); mySiege.GetDefendingPlayer().mySieges.Add(mySiege.siegeID); // add to fief target.siege = mySiege.siegeID; // reduce expenditures in fief, except for garrison target.infrastructureSpendNext = 0; target.keepSpendNext = 0; target.officialsSpendNext = 0; // update days (NOTE: siege.days will be updated in syncDays) mySiege.totalDays++; // sychronise days mySiege.SyncSiegeDays(mySiege.days - 1); // =================== construct and send JOURNAL ENTRY // ID uint entryID = Globals_Game.GetNextJournalEntryID(); // personae List <string> tempPersonae = new List <string>(); tempPersonae.Add("all|all"); tempPersonae.Add(mySiege.GetDefendingPlayer().charID + "|fiefOwner"); tempPersonae.Add(mySiege.GetBesiegingPlayer().charID + "|attackerOwner"); tempPersonae.Add(attacker.GetLeader().charID + "|attackerLeader"); // get defenderLeader Character defenderLeader = defenderGarrison.GetLeader(); if (defenderLeader != null) { tempPersonae.Add(defenderLeader.charID + "|defenderGarrisonLeader"); } // get additional defending leader Character addDefendLeader = null; if (defenderAdditional != null) { addDefendLeader = defenderAdditional.GetLeader(); if (addDefendLeader != null) { tempPersonae.Add(addDefendLeader.charID + "|defenderAdditionalLeader"); } } string[] siegePersonae = tempPersonae.ToArray(); // location string siegeLocation = mySiege.GetFief().id; // description string[] fields = new string[6]; fields[0] = mySiege.GetBesiegingPlayer().firstName + " " + mySiege.GetBesiegingPlayer().familyName; fields[1] = attacker.GetLeader().firstName + " " + attacker.GetLeader().familyName; fields[2] = mySiege.GetFief().name; fields[3] = mySiege.GetDefendingPlayer().firstName + " " + mySiege.GetDefendingPlayer().familyName; fields[4] = fields[5] = ""; if (defenderLeader != null) { fields[4] = "The defending garrison is led by " + defenderLeader.firstName + " " + defenderLeader.familyName + "."; } if (addDefendLeader != null) { fields[5] = "Additional defending forces are led by " + addDefendLeader.firstName + " " + addDefendLeader.familyName + "."; } ProtoMessage siege = new ProtoMessage(); siege.MessageFields = fields; siege.ResponseType = DisplayMessages.PillageInitiateSiege; // put together new journal entry JournalEntry siegeResult = new JournalEntry(entryID, Globals_Game.clock.currentYear, Globals_Game.clock.currentSeason, siegePersonae, "siege", siege, loc: siegeLocation); // add new journal entry to pastEvents Globals_Game.AddPastEvent(siegeResult); return(mySiege); }
/// <summary> /// Calculates the outcome of the pillage of a fief by an army /// </summary> /// <param name="f">The fief being pillaged</param> /// <param name="a">The pillaging army</param> /// <param name="circumstance">The circumstance under which the fief is being pillaged</param> public static ProtoPillageResult ProcessPillage(Fief f, Army a, string circumstance = "pillage") { ProtoPillageResult pillageResult = new ProtoPillageResult(); double thisLoss = 0; double moneyPillagedTotal = 0; double moneyPillagedOwner = 0; double pillageMultiplier = 0; // get army leader Character armyLeader = a.GetLeader(); // get pillaging army owner (receives a proportion of total spoils) PlayerCharacter armyOwner = a.GetOwner(); pillageResult.fiefID = f.id; // get garrison leader (to add to journal entry) Character defenderLeader = null; if (f.bailiff != null) { defenderLeader = f.bailiff; } // calculate pillageMultiplier (based on no. pillagers per 1000 population) pillageMultiplier = a.CalcArmySize() / (f.population / 1000); // calculate days taken for pillage double daysTaken = Globals_Game.myRand.Next(7, 16); if (daysTaken > a.days) { daysTaken = a.days; } // update army days armyLeader.AdjustDays(daysTaken); pillageResult.daysTaken = daysTaken; // % population loss thisLoss = (0.007 * pillageMultiplier); // ensure is between 1%-20% if (thisLoss < 1) { thisLoss = 1; } else if (thisLoss > 20) { thisLoss = 20; } // apply population loss pillageResult.populationLoss = Convert.ToInt32((f.population * (thisLoss / 100))); f.population -= Convert.ToInt32((f.population * (thisLoss / 100))); // % treasury loss if (!circumstance.Equals("quellRebellion")) { thisLoss = (0.2 * pillageMultiplier); // ensure is between 1%-80% if (thisLoss < 1) { thisLoss = 1; } else if (thisLoss > 80) { thisLoss = 80; } // apply treasury loss if (f.Treasury > 0) { pillageResult.treasuryLoss = Convert.ToInt32((f.Treasury * (thisLoss / 100))); f.AdjustTreasury(-Convert.ToInt32((f.Treasury * (thisLoss / 100)))); } } // % loyalty loss thisLoss = (0.33 * pillageMultiplier); // ensure is between 1%-20% if (thisLoss < 1) { thisLoss = 1; } else if (thisLoss > 20) { thisLoss = 20; } // apply loyalty loss pillageResult.loyaltyLoss = (f.loyalty * (thisLoss / 100)); f.loyalty -= (f.loyalty * (thisLoss / 100)); // % fields loss thisLoss = (0.01 * pillageMultiplier); // ensure is between 1%-20% if (thisLoss < 1) { thisLoss = 1; } else if (thisLoss > 20) { thisLoss = 20; } // apply fields loss pillageResult.fieldsLoss = (f.fields * (thisLoss / 100)); f.fields -= (f.fields * (thisLoss / 100)); // % industry loss thisLoss = (0.01 * pillageMultiplier); // ensure is between 1%-20% if (thisLoss < 1) { thisLoss = 1; } else if (thisLoss > 20) { thisLoss = 20; } // apply industry loss pillageResult.industryLoss = (f.industry * (thisLoss / Convert.ToDouble(100))); f.industry -= (f.industry * (thisLoss / 100)); // money pillaged (based on GDP) thisLoss = (0.032 * pillageMultiplier); // ensure is between 1%-50% if (thisLoss < 1) { thisLoss = 1; } else if (thisLoss > 50) { thisLoss = 50; } // calculate base amount pillaged based on fief GDP double baseMoneyPillaged = (f.keyStatsCurrent[1] * (thisLoss / 100)); moneyPillagedTotal = baseMoneyPillaged; pillageResult.baseMoneyPillaged = baseMoneyPillaged; // factor in no. days spent pillaging (get extra 5% per day > 7) int daysOver7 = Convert.ToInt32(daysTaken) - 7; if (daysOver7 > 0) { for (int i = 0; i < daysOver7; i++) { moneyPillagedTotal += (baseMoneyPillaged * 0.05); } pillageResult.bonusMoneyPillaged = moneyPillagedTotal - baseMoneyPillaged; pillageResult.daysTaken = daysOver7; } // check for jackpot // generate randomPercentage to see if hit the jackpot int myRandomPercent = Globals_Game.myRand.Next(101); if (myRandomPercent <= 30) { // generate random int to multiply amount pillaged int myRandomMultiplier = Globals_Game.myRand.Next(3, 11); pillageResult.jackpot = moneyPillagedTotal * myRandomMultiplier - moneyPillagedTotal; moneyPillagedTotal = moneyPillagedTotal * myRandomMultiplier; } // check proportion of money pillaged goes to army owner (based on stature) double proportionForOwner = 0.05 * armyOwner.CalculateStature(); moneyPillagedOwner = (moneyPillagedTotal * proportionForOwner); pillageResult.moneyPillagedOwner = moneyPillagedOwner; // apply to army owner's home fief treasury armyOwner.GetHomeFief().AdjustTreasury(Convert.ToInt32(moneyPillagedOwner)); // apply loss of stature to army owner if fief has same language if (armyOwner.language.id == f.language.id) { armyOwner.AdjustStatureModifier(-0.3); pillageResult.statureModifier = (-0.3); } else if (armyOwner.language.baseLanguage.id == f.language.baseLanguage.id) { armyOwner.AdjustStatureModifier(-0.2); pillageResult.statureModifier = (-0.2); } // set isPillaged for fief f.isPillaged = true; // =================== construct and send JOURNAL ENTRY // ID uint entryID = Globals_Game.GetNextJournalEntryID(); // personae List <string> tempPersonae = new List <string>(); tempPersonae.Add(f.owner.charID + "|fiefOwner"); tempPersonae.Add(armyOwner.charID + "|attackerOwner"); if (armyLeader != null) { tempPersonae.Add(armyLeader.charID + "|attackerLeader"); } if ((defenderLeader != null) && (!circumstance.Equals("quellRebellion"))) { tempPersonae.Add(defenderLeader.charID + "|defenderLeader"); } if (circumstance.Equals("quellRebellion")) { tempPersonae.Add("all|all"); } string[] pillagePersonae = tempPersonae.ToArray(); // location string pillageLocation = f.id; // type string type = ""; if (circumstance.Equals("pillage")) { type += "pillage"; } else if (circumstance.Equals("quellRebellion")) { type += "rebellionQuelled"; } if (circumstance.Equals("pillage")) { pillageResult.isPillage = true; } else if (circumstance.Equals("quellRebellion")) { pillageResult.isPillage = false; } pillageResult.fiefName = f.name; pillageResult.fiefOwner = f.owner.firstName + " " + f.owner.familyName; if ((circumstance.Equals("pillage")) && (defenderLeader != null)) { if (f.owner != defenderLeader) { pillageResult.defenderLeader = defenderLeader.firstName + " " + defenderLeader.familyName; } } pillageResult.armyOwner = armyOwner.firstName + " " + armyOwner.familyName; if (armyLeader != null) { pillageResult.armyLeader = armyLeader.firstName + " " + armyLeader.familyName; } // put together new journal entry JournalEntry pillageEntry = new JournalEntry(pillageResult, entryID, Globals_Game.clock.currentYear, Globals_Game.clock.currentSeason, pillagePersonae, type, loc: pillageLocation); // add new journal entry to pastEvents Globals_Game.AddPastEvent(pillageEntry); return(pillageResult); }