Ejemplo n.º 1
0
        /// <summary>
        /// Checks all in-progress buildings and researches of the country,
        /// and adds any completed ones to it. Does not delete in progress values that are completed.
        /// This method does not perform any safety check regarding the amount of buildings or researches!
        /// </summary>
        /// <param name="country">The country to build in. The buildings, researches,
        /// in progress buildings, researches, and their buildings and researches, must be included.</param>
        /// <returns>If the building could be started.</returns>
        protected void CheckAddCompleted(Model.Entities.Country country)
        {
            var researches = country.InProgressResearches
                             .Where(r => r.TimeLeft == 0)
                             .GroupBy(r => r.Research)
                             .ToList();

            if (researches.Count > 0)
            {
                foreach (var research in researches)
                {
                    var existing = country.Researches.FirstOrDefault(r => r.Research.Equals(research.Key));

                    // Add a new research, or update an existing one
                    if (existing == null)
                    {
                        country.Researches.Add(new CountryResearch
                        {
                            Research = research.Key,
                            Count    = research.Count()
                        });
                    }
                    else
                    {
                        existing.Count += research.Count();
                    }
                }
            }

            // Get and complete buildings
            var buildings = country.InProgressBuildings
                            .Where(r => r.TimeLeft == 0)
                            .GroupBy(b => b.Building)
                            .ToList();

            if (buildings.Count > 0)
            {
                foreach (var building in buildings)
                {
                    var existing = country.Buildings.FirstOrDefault(b => b.Building.Equals(building.Key));

                    // Add a new building, or update an existing one
                    if (existing == null)
                    {
                        country.Buildings.Add(new CountryBuilding()
                        {
                            Building = building.Key,
                            Count    = building.Count()
                        });
                    }
                    else
                    {
                        existing.Count += building.Count();
                    }
                }
            }
        }
Ejemplo n.º 2
0
        /// <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);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Deletes units until the supply / maintenance consumption can be satisfied by the country.
        /// Starts with the cheapest units. The commands, divisions and units must be included.
        /// </summary>
        /// <param name="country">The country to delete from.</param>
        protected void DesertUnits(Model.Entities.Country country)
        {
            long pearlUpkeep = 0;
            long coralUpkeep = 0;

            foreach (var comm in country.Commands)
            {
                foreach (var div in comm.Divisions)
                {
                    pearlUpkeep += div.Count * div.Unit.MaintenancePearl;
                    coralUpkeep += div.Count * div.Unit.MaintenanceCoral;
                }
            }

            if (coralUpkeep > country.Corals || pearlUpkeep > country.Pearls)
            {
                long pearlDeficit = Math.Max(pearlUpkeep - country.Pearls, 0);
                long coralDeficit = Math.Max(coralUpkeep - country.Corals, 0);

                foreach (var div in country.Commands.SelectMany(c => c.Divisions).OrderBy(d => d.Unit.CostPearl))
                {
                    long requiredPearlReduction = (long)Math.Ceiling((double)pearlDeficit / div.Unit.MaintenancePearl);
                    long requiredCoralReduction = (long)Math.Ceiling((double)coralDeficit / div.Unit.MaintenanceCoral);

                    int desertedAmount = (int)Math.Min(Math.Max(requiredCoralReduction, requiredPearlReduction), div.Count);

                    div.Count -= desertedAmount;

                    pearlDeficit -= desertedAmount * div.Unit.MaintenancePearl;
                    pearlUpkeep  -= desertedAmount * div.Unit.MaintenancePearl;
                    coralDeficit -= desertedAmount * div.Unit.MaintenanceCoral;
                    coralUpkeep  -= desertedAmount * div.Unit.MaintenanceCoral;

                    if (coralUpkeep <= country.Corals && pearlUpkeep <= country.Pearls)
                    {
                        break;
                    }
                }
            }

            pearlUpkeep = Math.Max(0, pearlUpkeep);
            coralUpkeep = Math.Max(0, coralUpkeep);

            country.Pearls -= pearlUpkeep;
            country.Corals -= coralUpkeep;
        }
Ejemplo n.º 4
0
        /// <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);
            }
        }
Ejemplo n.º 5
0
        public async Task CreateAsync(string username, string countryName, CancellationToken turnEndWaitToken)
        {
            using (var lck = await TurnEndLock.ReaderLockAsync(turnEndWaitToken))
            {
                var user = await Context.Users.SingleAsync(u => u.UserName == username);

                var globals = await Context.GlobalValues
                              .Include(g => g.FirstStartingBuilding)
                              .Include(g => g.SecondStartingBuilding)
                              .SingleAsync();

                var country = new Model.Entities.Country()
                {
                    Name         = countryName,
                    ParentUser   = user,
                    Corals       = globals.StartingCorals,
                    Pearls       = globals.StartingPearls,
                    Score        = -1,
                    Rank         = -1,
                    CreatedRound = globals.Round
                };

                var defenders = new Command {
                    ParentCountry = country, TargetCountry = country
                };

                Context.Countries.Add(country);
                Context.Commands.Add(defenders);
                Context.CountryBuildings.AddRange(
                    new CountryBuilding {
                    ParentCountry = country, Count = 1, Building = globals.FirstStartingBuilding
                },
                    new CountryBuilding {
                    ParentCountry = country, Count = 1, Building = globals.SecondStartingBuilding
                });

                await Context.SaveChangesAsync();
            }
        }
Ejemplo n.º 6
0
        /// <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);
            }
        }