示例#1
0
 public override void PlaceCombatants(SafeDictionary <ICombatant, IntVector2> locations)
 {
     if (Sector.SpaceObjects.OfType <WarpPoint>().Any())
     {
         // HACK - warp point in sector, assume someone warped
         // TODO - do this for warp point exits instead since warp points may be one way
         // warp battles start with everyone mashed together to allow blockades
         foreach (var c in Combatants.OrderByDescending(q => q.Size))
         {
             PlaceCombatant(locations, 0, 0, c);
         }
     }
     else
     {
         // place all combatants at the points of a regular polygon
         var sideLength = 20 + (int)Math.Ceiling((double)Combatants.GroupBy(q => q.Owner).Max(q => q.Count()));                 // make sure no one can shoot each other at the start
         // https://stackoverflow.com/questions/32169875/calculating-the-coordinates-of-a-regular-polygon-given-its-center-and-its-side-l
         var radius = sideLength / (2 * Sin(PI / Empires.Count()));
         var combs  = Combatants.ToArray();
         for (int i = 0; i < Empires.Count(); i++)
         {
             var x = radius * Cos(PI / Empires.Count() * (1 + 2 * i));
             var y = radius * Sin(PI / Empires.Count() * (1 + 2 * i));
             foreach (var comb in Combatants.Where(q => q.Owner == Empires.ElementAt(i)).OrderByDescending(q => q.Size))
             {
                 PlaceCombatant(locations, x, y, comb);
             }
         }
     }
 }
示例#2
0
        /// <summary>
        /// Resolves the battle.
        /// </summary>
        public void Resolve()
        {
            // update memories
            foreach (var sobj in StarSystem.SpaceObjects.Where(x => !x.IsMemory).ToArray())
            {
                sobj.UpdateEmpireMemories();
            }

            Current.Add(this);
            var reloads = new SafeDictionary <Component, double>();
            var seekers = new Dictionary <Seeker, int>();

            // let all combatants scan each other
            foreach (var c in Combatants)
            {
                c.UpdateEmpireMemories();
            }

            for (int i = 0; i < Mod.Current.Settings.SpaceCombatTurns; i++)
            {
                LogRound(i + 1);
                // TODO - real 2D combat mechanics
                foreach (var seeker in seekers.Keys.ToArray())
                {
                    seekers[seeker]--;
                    if (seekers[seeker] <= 0)
                    {
                        seekers.Remove(seeker);
                        var minrng = seeker.LaunchingComponent.Template.WeaponMinRange;
                        var maxrng = seeker.LaunchingComponent.Template.WeaponMinRange;
                        var range  = Dice.Next(maxrng - minrng) + minrng;                        // just pick a random valid range
                        var shot   = new Shot(seeker.LaunchingCombatant, seeker.LaunchingComponent, seeker.Target, range);
                        Log.Add(seeker.CreateLogMessage(seeker + " detonates! " + seeker.Target + " takes " + shot.FullDamage + " damage."));
                        seeker.Target.TakeDamage(new Hit(shot, seeker.Target, seeker.Damage.Value));
                    }
                    else
                    {
                        Log.Add(seeker.CreateLogMessage(seeker + " moves closer to " + seeker.Target + " (" + seekers[seeker] + " rounds to detonation)"));
                    }
                }
                foreach (var launcher in Combatants.ToArray())
                {
                    // find launchable units
                    var unitsToLaunch = new List <SpaceVehicle>();
                    if (launcher is Planet)
                    {
                        // planets can launch infinite units per turn
                        var p = (Planet)launcher;
                        if (p.Cargo != null && p.Cargo.Units != null)
                        {
                            unitsToLaunch.AddRange(p.Cargo.Units.OfType <SpaceVehicle>());
                        }
                    }
                    else if (launcher is ICargoTransferrer)
                    {
                        // ships, etc. can launch units based on abilities
                        var ct = (ICargoTransferrer)launcher;
                        foreach (var vt in Enum.GetValues(typeof(VehicleTypes)).Cast <VehicleTypes>().Distinct())
                        {
                            var rate = ct.GetAbilityValue("Launch/Recover " + vt.ToSpacedString() + "s").ToInt();
                            unitsToLaunch.AddRange(ct.Cargo.Units.Where(u => u.Design.VehicleType == vt).OfType <SpaceVehicle>().Take(rate));
                        }
                    }

                    // launch them temporarily for combat
                    foreach (var unit in unitsToLaunch)
                    {
                        Combatants.Add(unit);
                    }
                }
                foreach (var attacker in Combatants.Shuffle(Dice).Where(sobj => sobj.Weapons.Any()).ToArray())
                {
                    if (!attacker.IsAlive)
                    {
                        continue;
                    }

                    var defenders = Combatants.Where(sobj => attacker.CanTarget(sobj) && sobj.IsAlive);
                    if (!defenders.Any())
                    {
                        continue;                         // no one to shoot at
                    }
                    var defender = defenders.PickRandom(Dice);

                    int dmg = 0;
                    foreach (var weapon in attacker.Weapons.Where(w => w.CanTarget(defender)))
                    {
                        while (reloads[weapon] <= 0)
                        {
                            // fire
                            var winfo = weapon.Template.ComponentTemplate.WeaponInfo;
                            if (winfo is SeekingWeaponInfo)
                            {
                                // launch a seeker
                                var swinfo = (SeekingWeaponInfo)winfo;
                                var seeker = new Seeker(this, attacker.Owner, attacker, weapon, defender);
                                seekers.Add(seeker, 20 / swinfo.SeekerSpeed);
                                LogLaunch(seeker);
                            }
                            else
                            {
                                // direct fire
                                var minrng = weapon.Template.WeaponMinRange;
                                var maxrng = weapon.Template.WeaponMinRange;
                                var range  = Dice.Next(maxrng - minrng) + minrng;                                // just pick a random valid range
                                var shot   = new Shot(attacker, weapon, defender, range);
                                dmg += shot.FullDamage;
                                defender.TakeDamage(new Hit(shot, defender, weapon.Template.GetWeaponDamage(range)));
                            }
                            // TODO - mounts that affect reload rate?
                            reloads[weapon] += weapon.Template.ComponentTemplate.WeaponInfo.ReloadRate;
                        }

                        // reload
                        reloads[weapon] -= 1;
                    }
                    LogSalvo(attacker, defender, dmg);
                }
            }

            // validate fleets since some ships might have died
            foreach (var fleet in Sector.SpaceObjects.OfType <Fleet>())
            {
                fleet.Validate();
            }

            // replenish combatants' shields
            foreach (var combatant in Sector.SpaceObjects.OfType <ICombatant>())
            {
                combatant.ReplenishShields();
            }

            // mark battle complete
            Current.Remove(this);
            Previous.Add(this);

            // update memories
            foreach (var sobj in Combatants.OfType <ISpaceObject>().Where(x => !x.IsMemory).ToArray())
            {
                foreach (var emp in Empires)
                {
                    emp.UpdateMemory(sobj);;
                }
            }
        }