/// <summary> /// If two civilizations spawn in the same area, they must fight to /// determine who may settle there. /// </summary> /// <param name="newLair">the challenger to the conflict</param> /// <returns>Battle object holds the state results of the conflict. If /// the spot is not challenged at all, then null is returned. /// </returns> private Battle ResolveSettlementConflicts(Lair newLair) { foreach (var lair in LairList) { if (newLair.SubhexIndex != lair.SubhexIndex) { continue; } var battle = new Battle(_worldMap); battle.ResolveBattle(this, this, newLair, lair); return(battle); } return(null); }
/// <summary> /// New monsters move in from the edge of the map. /// </summary> /// <param name="hex"></param> public void ResolveOutsideSingleHexMigration(HexData hex) { // only for edge chances int numEdges = _worldMap.NumberOutsideEdges(hex); if (numEdges <= 0) { return; } // "5% chance per edge" int chance = numEdges * 5; int roll = _dice.Roll(1, 100); if (roll > chance) { return; } // Are they invaders, or settlers? var invaders = new Civilization(); invaders.InitializeAsRandomCiv(hex.Tier); // Depends on where they land: int subHexLoc = _dice.Roll(1, HexData.SUB_HEXES); var results = ResolveInvasionConflicts(hex, invaders, subHexLoc); if (results is null) { // no battle. settle in unclaimed land. var lair = new Lair(); lair.InitializeAsSettlerLair(invaders, subHexLoc, hex); lair.Treasure = 1; hex.LairList.Add(lair); return; } if ( (results.AttackerState == Battle.CombatantState.COMBATANT_STATE_ELIMINATED) || (results.AttackerState == Battle.CombatantState.COMBATANT_STATE_ROUTED) ) { // invaders have been dealt with. return; } // else the invaders win, and have been moved into their new lair. }
private void _recordBattleReport(string record, Civilization attacker, Civilization defender, Lair defenderBase) { record += " Battle lasted " + NumRounds + " rounds. " + attacker.GetFullName() + " survivors: " + (_attackerStartingForces - _defenderScore) + ". " + defender.GetFullName() + " survivors: " + (_defenderStartingForces - _attackerScore) + ". Final tally: " + attacker.GetFullName() + ": " + attacker.Patricians.Members + " vs " + defender.GetFullName() + ": " + defender.Patricians.Members; attacker.History.addRecord(record); defender.History.addRecord(record, isLogged: false); defenderBase.History.addRecord(record, isLogged: false); }
private void _doMigration(Lair lair, HexData hex) { if (lair.IsRuins()) { return; } string record = "The " + lair.HomeCiv.GetPluralName() + " are migrating to a new home."; lair.HomeCiv.History.addRecord(record); HexData targetHex; do { int migrationDirection = _dice.Roll(1, 6); var neighborLocation = hex.FindNeighborByIndex(migrationDirection); targetHex = _worldMap.GetHexByCoordinates(neighborLocation); } while (targetHex is null); int subHexIndex = _dice.Roll(1, HexData.SUB_HEXES); var existingLair = targetHex.GetLairAtLocation(subHexIndex); if (existingLair is null) { // Free real estate! var newLair = new Lair(); newLair.InitializeAsSettlerLair(lair.HomeCiv, subHexIndex, targetHex); targetHex.LairList.Add(newLair); lair.ForceAbandon(); // move the treasure newLair.Treasure = lair.Treasure; lair.Treasure = 0; return; } // Going to have to fight for the space. var battle = new Battle(_worldMap); battle.ResolveBattle(hex, targetHex, lair, existingLair); }
/// <summary> /// The main battle resolution /// Combatants must come from a lair in a hex. /// </summary> /// <param name="attackerLands"></param> /// <param name="defenderLands"></param> /// <param name="attackerBase"></param> /// <param name="defenderBase"></param> public void ResolveBattle(HexData attackerLands, HexData defenderLands, Lair attackerBase, Lair defenderBase) { if (attackerBase.IsRuins()) { // no attackers! do not pass! AttackerState = CombatantState.COMBATANT_STATE_ELIMINATED; return; } Civilization attacker = attackerBase.HomeCiv; string record; if (defenderBase.IsRuins()) { record = (defenderBase.GetFullName() + " is found abandoned by the " + attacker.GetPluralName()); defenderBase.History.addRecord(record); attacker.History.addRecord(record, isLogged: false); defenderBase.MoveCivIn(attacker); attackerBase.ForceAbandon(); defenderBase.Treasure += attackerBase.Treasure; attackerBase.Treasure = 0; DefenderState = CombatantState.COMBATANT_STATE_ELIMINATED; return; } Civilization defender = defenderBase.HomeCiv; // since this method is only called on initial spawn, then the // rule is to merge if two of the same race spawn in the same // hex area. if (attacker.Patricians.BaseAncestry.Name == defender.Patricians.BaseAncestry.Name) { defender.JoinOurCivilization(attacker.GetFullName()); defender.Patricians.Members += attacker.Patricians.Members; attacker.DissolvePopulation(); return; } record = "The " + attacker.GetPluralName() + " are attacking the " + defender.GetPluralName() + " at " + defenderBase.GetFullName() + "!"; attacker.History.addRecord(record); defender.History.addRecord(record, isLogged: false); defenderBase.History.addRecord(record, isLogged: false); GatherAttackers(attacker, attackerLands, defender.GetFullName()); GatherDefenders(defender, defenderLands, attacker.GetFullName()); _attackerStartingForces = GetTotalCombatants(AttackerList); _defenderStartingForces = GetTotalCombatants(DefenderList); _executeMainBattleLoop(); // recover from battle ResolveSurvivors(_attackerStartingForces, AttackerList); ResolveSurvivors(_defenderStartingForces, DefenderList); // determine outcome of battle. bool isAttackerLost = (AttackerState == CombatantState.COMBATANT_STATE_ELIMINATED) || (AttackerState == CombatantState.COMBATANT_STATE_ROUTED); bool isDefenderLost = (DefenderState == CombatantState.COMBATANT_STATE_ELIMINATED) || (DefenderState == CombatantState.COMBATANT_STATE_ROUTED); bool isBothLost = isAttackerLost && isDefenderLost; bool isMutualDestruction = (DefenderState == CombatantState.COMBATANT_STATE_ELIMINATED && AttackerState == CombatantState.COMBATANT_STATE_ELIMINATED); if (isMutualDestruction) { // mutual destruction is highly unlikely, but not impossible. record = "The " + attacker.GetPluralName() + " and the " + defender.GetPluralName() + " have achieved mutual destruction at " + defenderBase.GetFullName() + "!"; attacker.DissolvePopulation(); defender.DissolvePopulation(); } else if (isBothLost) { if (DefenderState == CombatantState.COMBATANT_STATE_ELIMINATED) { // defenders lose record = "The " + defender.GetPluralName() + " have been defeated by the " + attacker.GetPluralName() + " at " + defenderBase.GetFullName() + "!"; string defenderBaseName = defenderBase.Name; MoveLosers(defender, defenderLands, defenderBaseName); defenderBase.MoveCivIn(attacker); attackerBase.ForceAbandon(); defenderBase.Treasure += attackerBase.Treasure; attackerBase.Treasure = 0; } else { record = "The " + attacker.GetPluralName() + " have been repelled by the " + defender.GetPluralName() + " at " + defenderBase.GetFullName() + "!"; string attackerBaseName = attackerBase.Name; // It's interesting that attackers don't go back home. MoveLosers(attacker, attackerLands, attackerBaseName); attackerBase.ForceAbandon(); } } else if (isDefenderLost) { // defender loses! record = "The " + defender.GetPluralName() + " have been defeated by the " + attacker.GetPluralName() + " at " + defenderBase.GetFullName() + "!"; string defenderBaseName = defenderBase.Name; MoveLosers(defender, defenderLands, defenderBaseName); defenderBase.MoveCivIn(attacker); attackerBase.ForceAbandon(); defenderBase.Treasure += attackerBase.Treasure; attackerBase.Treasure = 0; } else // attacker lost { // attacker loses! record = "The " + attacker.GetPluralName() + " have been repelled by the " + defender.GetPluralName() + " at " + defenderBase.GetFullName() + "!"; string attackerBaseName = attackerBase.Name; // It's interesting that attackers don't go back home. MoveLosers(attacker, attackerLands, attackerBaseName); attackerBase.ForceAbandon(); } _recordBattleReport(record, attacker, defender, defenderBase); // any lingering civs with zero population are removed. _worldMap.CleanOutRuins(); }
/// <summary> /// Overloaded function for determining battles started by outside /// or homeless invaders. /// </summary> /// <param name="attacker"></param> /// <param name="defenderLands"></param> /// <param name="defenderBase"></param> public void ResolveBattle(Civilization attacker, HexData defenderLands, Lair defenderBase) { string record; if (defenderBase.IsRuins()) { record = (defenderBase.GetFullName() + " is found abandoned by the " + attacker.GetPluralName()); defenderBase.History.addRecord(record); attacker.History.addRecord(record, isLogged: false); defenderBase.MoveCivIn(attacker); defenderBase.Treasure += 1; DefenderState = CombatantState.COMBATANT_STATE_ELIMINATED; return; } Civilization defender = defenderBase.HomeCiv; record = "The " + attacker.GetPluralName() + " are attacking the " + defender.GetPluralName() + " at " + defenderBase.GetFullName() + "!"; attacker.History.addRecord(record); defender.History.addRecord(record, isLogged: false); defenderBase.History.addRecord(record, isLogged: false); AttackerList.Add(attacker); GatherDefenders(defender, defenderLands, attacker.GetFullName()); _attackerStartingForces = GetTotalCombatants(AttackerList); _defenderStartingForces = GetTotalCombatants(DefenderList); _executeMainBattleLoop(); // recover from battle ResolveSurvivors(_attackerStartingForces, AttackerList); ResolveSurvivors(_defenderStartingForces, DefenderList); // determine outcome of battle. bool isAttackerLost = (AttackerState == CombatantState.COMBATANT_STATE_ELIMINATED) || (AttackerState == CombatantState.COMBATANT_STATE_ROUTED); bool isDefenderLost = (DefenderState == CombatantState.COMBATANT_STATE_ELIMINATED) || (DefenderState == CombatantState.COMBATANT_STATE_ROUTED); bool isBothLost = isAttackerLost && isDefenderLost; bool isMutualDestruction = (DefenderState == CombatantState.COMBATANT_STATE_ELIMINATED && AttackerState == CombatantState.COMBATANT_STATE_ELIMINATED); if (isMutualDestruction) { // mutual destruction is highly unlikely, but not impossible. record = "The " + attacker.GetPluralName() + " and the " + defender.GetPluralName() + " have achieved mutual destruction at " + defenderBase.GetFullName() + "!"; attacker.DissolvePopulation(); defender.DissolvePopulation(); } else if (isBothLost) { if (DefenderState == CombatantState.COMBATANT_STATE_ELIMINATED) { // defender loses! record = "The " + defender.GetPluralName() + " have been defeated by the " + attacker.GetPluralName() + " at " + defenderBase.GetFullName() + "!"; string defenderBaseName = defenderBase.Name; MoveLosers(defender, defenderLands, defenderBaseName); defenderBase.MoveCivIn(attacker); defenderBase.Treasure += 1; } else { // attacker loses! record = "The " + attacker.GetPluralName() + " have been repelled by the " + defender.GetPluralName() + " at " + defenderBase.GetFullName() + "!"; // In this case, the attackers seek to allign themselves // possibly where they landed. MoveLosers(attacker, defenderLands, baseName: ""); } } else if (isDefenderLost) { // defender loses! record = "The " + defender.GetPluralName() + " have been defeated by the " + attacker.GetPluralName() + " at " + defenderBase.GetFullName() + "!"; string defenderBaseName = defenderBase.Name; MoveLosers(defender, defenderLands, defenderBaseName); defenderBase.MoveCivIn(attacker); defenderBase.Treasure += 1; } else // attacker lost { // attacker loses! record = "The " + attacker.GetPluralName() + " have been repelled by the " + defender.GetPluralName() + " at " + defenderBase.GetFullName() + "!"; // In this case, the attackers seek to allign themselves // possibly where they landed. MoveLosers(attacker, defenderLands, baseName: ""); } _recordBattleReport(record, attacker, defender, defenderBase); // any lingering civs with zero population are removed. _worldMap.CleanOutRuins(); }
/// <summary> /// Sets this hex up with random components /// </summary> /// <param name="x"></param> /// <param name="y"></param> public void InitializeAsRandomHex(int x, int y, Tuple <int, int> origin, int tierWidth) { var nameGen = new RandomName(); Name = nameGen.CreateWord(); XCoord = x; YCoord = y; _setTier(origin.Item1, origin.Item2, tierWidth); var creatureSource = AncestryIndex.Instance; creatureSource.LoadAllSources(); // init nature int numNatural = _dice.Roll(2, 4) + 1; for (int i = 0; i < numNatural; ++i) { var naturalCreature = creatureSource.GetRandomNaturalAncestry(Tier); var tier = Tier; while (naturalCreature is null && tier > 0) { --tier; naturalCreature = creatureSource.GetRandomAncestry(tier); } if (naturalCreature is null) { break; } NaturalEncounterPool.Add(naturalCreature.Name); } // init dungeons int numMonsters = _dice.Roll(2, 4) + 1; for (int i = 0; i < numMonsters; ++i) { var dungeonLurkers = creatureSource.GetRandomDungeonAncestry(Tier); var tier = Tier; while (dungeonLurkers is null && tier > 0) { --tier; dungeonLurkers = creatureSource.GetRandomDungeonAncestry(tier); } if (dungeonLurkers is null) { break; } DungeonEcologyPool.Add(dungeonLurkers.Name); } // init lairs int numLairs = _dice.Roll(1, 6) - 1; for (int i = 0; i < numLairs; ++i) { var lair = new Lair(); lair.InitializeAsRandomLair(this); // resolve any conflicting locations Battle results = ResolveSettlementConflicts(lair); if (results is null) { // there was no battle. Just add the new lair. LairList.Add(lair); continue; } if ( (results.AttackerState == Battle.CombatantState.COMBATANT_STATE_ELIMINATED) || (results.AttackerState == Battle.CombatantState.COMBATANT_STATE_ROUTED) ) { // dont even save the lair. continue; } // attackers win, and are moved into existing lair. } string record = Name + " (" + x + ", " + y + ") has been discovered."; if (LairList.Count > 0) { record += " The following lairs are found within: "; for (int i = 0; i < LairList.Count; ++i) { var lair = LairList[i]; int lastIndex = LairList.Count - 1; int secondToLastIndex = lastIndex - 1; record += lair.GetFullName(); if (i == secondToLastIndex) { record += ", and "; continue; } if (i == lastIndex) { continue; } record += ", "; } } History.addRecord(record); }