/// <summary> /// Handles pre-turn claculations, like building and research completitions, and income. /// Returns the <see cref="CountryModifierBuilder"/> containing the modifications for the country. /// </summary> /// <param name="context">The <see cref="UnderSeaDatabaseContext"/> that is used to access the database.</param> /// <param name="country">The country to handle. The following must be included: Buildings, In progress buildings, /// researches, in progress researches, the buildings and researches of these, events, and the corresponding effects, commands, divisions and units.</param> /// <param name="globals">The <see cref="GlobalValue"/>s to use.</param> /// <param name="allEvents">The collection of all events that may occur.</param> /// <param name="cancel">The <see cref="CancellationToken"/> that can be used to cancel the operation.</param> /// <exception cref="ArgumentNullException">Thrown if an argument was null.</exception> public void HandlePreCombat(UnderSeaDatabaseContext context, Model.Entities.Country country, GlobalValue globals, IList <RandomEvent> allEvents) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (country == null) { throw new KeyNotFoundException(); } if (globals == null) { throw new ArgumentNullException(nameof(globals)); } // #1: Add a random event if (country.CreatedRound + globals.RandomEventGraceTimer <= globals.Round && rng.NextDouble() <= globals.RandomEventChance) { country.CurrentEvent = allEvents[rng.Next(allEvents.Count)]; } else if (country.CurrentEvent != null) { country.CurrentEvent = null; } // Apply permanent effects here var builder = country.ParseAllEffectForCountry(context, globals, Parsers, true); if (builder.WasEventIgnored) { country.CurrentEvent = null; } // #2: Tax country.Pearls += (long)Math.Round(builder.Population * globals.BaseTaxation * builder.TaxModifier + builder.PearlProduction); // #3: Coral (harvest) country.Corals += (long)Math.Round(builder.CoralProduction * builder.HarvestModifier); // #4: Pay soldiers DesertUnits(country); // #5: Add buildings that are completed CheckAddCompleted(country); }
/// <summary> /// Handles post-combat calculations for a country, like calculating the score, and merging commands into the defense command. /// </summary> /// <param name="context">The <see cref="UnderSeaDatabaseContext"/> that is used to access the database.</param> /// <param name="country">The country to handle. The commands, their divisions and units, buildings and researches, events, and their effects must be loaded.</param> /// <param name="globals">The <see cref="GlobalValue"/>s to use.</param> /// <exception cref="ArgumentNullException">Thrown if an argument was null.</exception> public void HandlePostCombat(UnderSeaDatabaseContext context, Model.Entities.Country country, GlobalValue globals) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (country == null) { throw new ArgumentNullException(nameof(country)); } if (globals == null) { throw new ArgumentNullException(nameof(globals)); } var builder = country.ParseAllEffectForCountry(context, globals, Parsers, false); long divisionScore = 0; foreach (var comm in country.Commands) { divisionScore += comm.Divisions.Sum(d => d.Count); } country.Score = (long)Math.Round( builder.Population * globals.ScorePopulationMultiplier + country.Buildings.Count * globals.ScoreBuildingMultiplier + divisionScore * globals.ScoreUnitMultiplier + country.Researches.Count * globals.ScoreResearchMultiplier); // Merge all attacking commands into the defense command, delete attacking commands, and add the loot var defenders = country.GetAllDefending(); foreach (var attack in country.Attacks.Where(x => x.DidAttackerWin && x.Round == globals.Round)) { country.Pearls += attack.PearlLoot; country.Corals += attack.CoralLoot; } foreach (var attack in country.Commands.Where(c => c.Id != defenders.Id).ToList()) { attack.MergeInto(defenders, context); } }
/// <summary> /// Handles combat calculations for all incoming attacks of a country. The order of the attacks is randomized. /// </summary> /// <param name="context">The <see cref="UnderSeaDatabaseContext"/> that is used to access the database.</param> /// <param name="country">The country to handle. The commands, incoming attacks, the attack's divisions, parent country, /// parent country builidings, researches, events, and effects must be loaded.</param> /// <param name="globals">The <see cref="GlobalValue"/>s to use.</param> /// <exception cref="ArgumentNullException">Thrown if an argument was null.</exception> public void HandleCombat(UnderSeaDatabaseContext context, Model.Entities.Country country, GlobalValue globals) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (country == null) { throw new ArgumentNullException(nameof(country)); } if (globals == null) { throw new ArgumentNullException(nameof(globals)); } // Randomize the incoming attacks, excluding the defending forces var incomingAttacks = country.IncomingAttacks .Where(c => !c.ParentCountry.Equals(country)) .Select(c => new { Order = rng.Next(), Command = c }) .OrderBy(x => x.Order) .Select(x => x.Command) .ToList(); var defenders = country.GetAllDefending(); var builder = country.ParseAllEffectForCountry(context, globals, Parsers, false); foreach (var attack in incomingAttacks) { (double attackPower, double attackMods, double attackBase) = GetCurrentUnitPower(attack, globals, true, attack.ParentCountry.ParseAllEffectForCountry(context, globals, Parsers, false)); (double defensePower, double defenseMods, double defenseBase) = GetCurrentUnitPower(defenders, globals, false, builder); var losses = attackPower > defensePower ? CullUnits(defenders, globals.UnitLossOnLostBatle) : CullUnits(attack, globals.UnitLossOnLostBatle); var report = new CombatReport { Attacker = attack.ParentCountry, Defender = country, Attackers = attack.Divisions.Select(d => new Division { Count = d.Count, Unit = d.Unit }).ToList(), Defenders = defenders.Divisions.Select(d => new Division { Count = d.Count, Unit = d.Unit }).ToList(), TotalAttackPower = attackPower, TotalDefensePower = defensePower, AttackModifier = attackMods, DefenseModifier = defenseMods, BaseAttackPower = attackBase, BaseDefensePower = defenseBase, Round = globals.Round, PearlLoot = 0, CoralLoot = 0, Losses = losses }; if (attackPower > defensePower) { var pearlLoot = (long)Math.Round(country.Pearls * globals.LootPercentage); var coralLoot = (long)Math.Round(country.Corals * globals.LootPercentage); country.Pearls -= pearlLoot; country.Corals -= coralLoot; report.CoralLoot = coralLoot; report.PearlLoot = pearlLoot; } context.Reports.Add(report); } }