Example #1
0
        private Tuple <int, int> PlaceSquadVertically(BattleSquad squad, Tuple <int, int> bottomLeft, Tuple <int, int> squadBoxSize)
        {
            Tuple <int, int> startingLocation = new Tuple <int, int>(bottomLeft.Item1 + squadBoxSize.Item2 - 1,
                                                                     bottomLeft.Item2 + ((squadBoxSize.Item1 - 1) / 2));

            for (int i = 0; i < squad.Soldiers.Count; i++)
            {
                // 0th soldier goes in the coordinate given, then alternate to each side up to membersPerRow, then repeat in additional rows as necessary
                int xMod = i / squadBoxSize.Item1 * (squad.IsPlayerSquad ? -1 : 1);
                int yMod = ((i % squadBoxSize.Item1) + 1) / 2 * (i % 2 == 0 ? -1 : 1);
                if (squad.IsPlayerSquad)
                {
                    _playerSoldierIds.Add(squad.Soldiers[i].Soldier.Id);
                }
                else
                {
                    _opposingSoldierIds.Add(squad.Soldiers[i].Soldier.Id);
                }
                Tuple <int, int> location = new Tuple <int, int>(startingLocation.Item1 + xMod, startingLocation.Item2 + yMod);
                _soldierLocationMap[squad.Soldiers[i].Soldier.Id] = location;
                _locationSoldierMap[location] = squad.Soldiers[i].Soldier.Id;
                squad.Soldiers[i].Location    = location;
            }

            return(startingLocation);
        }
        private void ShootIfReasonable(BattleSoldier soldier, bool isMoving)
        {
            if (soldier.RangedWeapons.Count == 0)
            {
                return;
            }
            float         range    = _grid.GetNearestEnemy(soldier.Soldier.Id, out int closestEnemyId);
            BattleSquad   oppSquad = _opposingSoldierIdSquadMap[closestEnemyId];
            BattleSoldier target   = oppSquad.GetRandomSquadMember();

            range = _grid.GetDistanceBetweenSoldiers(soldier.Soldier.Id, target.Soldier.Id);
            // decide whether to shoot or aim
            Tuple <float, float, RangedWeapon> weaponProfile = ShouldShootAtRange(soldier, target, range, isMoving);

            if (weaponProfile.Item3 != null)
            {
                int shotsToFire = CalculateShotsToFire(weaponProfile.Item3, weaponProfile.Item1, weaponProfile.Item2);
                _shootActionBag.Add(new ShootAction(soldier, weaponProfile.Item3, target, range, shotsToFire, isMoving, _woundResolutionBag, _log));
            }
            else if (!isMoving)
            {
                // aim with longest ranged weapon
                _shootActionBag.Add(new AimAction(soldier, target, soldier.EquippedRangedWeapons.OrderByDescending(w => w.Template.MaximumRange).First(), _log));
            }
        }
        private void AddRetreatingActionsToBag(BattleSoldier soldier, BattleSquad soldierSquad)
        {
            float moveSpeed = soldier.GetMoveSpeed();

            int newY = (int)(soldierSquad.IsPlayerSquad ? -moveSpeed : moveSpeed);

            AddMoveAction(soldier, moveSpeed, new Tuple <int, int>(0, newY));

            // determine if soldier will shoot as he falls back
            ShootIfReasonable(soldier, true);
        }
Example #4
0
        private static List <BattleSquad> CreateBattleSquadList(IReadOnlyList <Squad> squads,
                                                                bool isPlayerSquad)
        {
            List <BattleSquad> battleSquadList = new List <BattleSquad>();

            foreach (Squad squad in squads)
            {
                BattleSquad bs = new BattleSquad(isPlayerSquad, squad);

                battleSquadList.Add(bs);
            }
            return(battleSquadList);
        }
Example #5
0
 public BattleSoldier(ISoldier soldier, BattleSquad squad)
 {
     Soldier               = soldier;
     BattleSquad           = squad;
     MeleeWeapons          = new List <MeleeWeapon>();
     RangedWeapons         = new List <RangedWeapon>();
     EquippedMeleeWeapons  = new List <MeleeWeapon>();
     EquippedRangedWeapons = new List <RangedWeapon>();
     Location              = null;
     Aim              = null;
     IsInMelee        = false;
     Stance           = Stance.Standing;
     CurrentSpeed     = 0;
     EnemiesTakenDown = 0;
 }
Example #6
0
        public void PlaceBattleSquad(BattleSquad squad, Tuple <int, int> bottomLeft, bool longHorizontal)
        {
            // if any squad member is already on the map, we have a problem
            if (squad.Soldiers.Any(s => _soldierLocationMap.ContainsKey(s.Soldier.Id)))
            {
                throw new InvalidOperationException(squad.Name + " has soldiers already on BattleGrid");
            }
            if (squad.Soldiers.Count == 0)
            {
                throw new InvalidOperationException("No soldiers in " + squad.Name + " to place");
            }
            Tuple <int, int> squadBoxSize = squad.GetSquadBoxSize();
            Tuple <int, int> startingLocation;

            if (longHorizontal)
            {
                startingLocation = PlaceSquadHorizontally(squad, bottomLeft, squadBoxSize);
            }
            else
            {
                startingLocation = PlaceSquadVertically(squad, bottomLeft, squadBoxSize);
            }
            OnSquadPlaced.Invoke(squad, startingLocation);
        }
        public void PrepareActions(BattleSquad squad)
        {
            int retreatVotes = 0;
            int advanceVotes = 0;
            int standVotes   = 0;
            int chargeVotes  = 0;

            // need some concept of squad disposition... stance, whether they're actively aiming
            // determine closest enemy
            // determine our optimal range
            // determine closest enemy optimal range
            // if the enemy wants to advance, we want to stay put, and vice versa
            // if we both want to get closer or both want to stay put, it's more interesting
            if (squad.IsInMelee)
            {
                // it doesn't really matter what the soldiers want to do, it's time to fight
                foreach (BattleSoldier soldier in squad.Soldiers)
                {
                    if (_grid.IsAdjacentToEnemy(soldier.Soldier.Id))
                    {
                        AddMeleeActionsToBag(soldier);
                    }
                    else
                    {
                        AddChargeActionsToBag(soldier);
                    }
                }
            }
            else
            {
                foreach (BattleSoldier soldier in squad.Soldiers)
                {
                    float       distance             = _grid.GetNearestEnemy(soldier.Soldier.Id, out int closestSoldierId);
                    BattleSquad closestSquad         = _opposingSoldierIdSquadMap[closestSoldierId];
                    float       targetSize           = closestSquad.GetAverageSize();
                    float       targetArmor          = closestSquad.GetAverageArmor();
                    float       targetCon            = closestSquad.GetAverageConstitution();
                    float       preferredHitDistance = CalculateOptimalDistance(soldier, targetSize, targetArmor, targetCon);
                    if (preferredHitDistance == -1)
                    {
                        // this soldier wants to run
                        retreatVotes++;
                    }
                    else if (preferredHitDistance == 0)
                    {
                        if (soldier.EquippedRangedWeapons.Count >= 1)
                        {
                            float desperateHitDistance = EstimateArmorPenDistance(soldier.EquippedRangedWeapons[0], targetArmor);
                            desperateHitDistance = Mathf.Min(desperateHitDistance, EstimateHitDistance(soldier.Soldier, soldier.EquippedRangedWeapons[0], targetSize, soldier.HandsFree));
                            if (desperateHitDistance > 0)
                            {
                                float targetPreferredDistance = CalculateOptimalDistance(closestSquad.GetRandomSquadMember(),
                                                                                         soldier.Soldier.Size,
                                                                                         soldier.Armor.Template.ArmorProvided,
                                                                                         soldier.Soldier.Constitution);

                                if (desperateHitDistance < targetPreferredDistance)
                                {
                                    // advance
                                    advanceVotes++;
                                }
                                else
                                {
                                    // don't advance
                                    standVotes++;
                                }
                            }
                            else
                            {
                                advanceVotes++;
                                chargeVotes++;
                            }
                        }
                        else
                        {
                            advanceVotes++;
                            chargeVotes++;
                        }
                    }
                    else if (distance > preferredHitDistance * 3)
                    {
                        advanceVotes++;
                    }
                    else
                    {
                        float targetPreferredDistance = CalculateOptimalDistance(closestSquad.GetRandomSquadMember(),
                                                                                 soldier.Soldier.Size,
                                                                                 soldier.Armor.Template.ArmorProvided,
                                                                                 soldier.Soldier.Constitution);

                        if (preferredHitDistance < targetPreferredDistance)
                        {
                            // advance
                            advanceVotes++;
                        }
                        else
                        {
                            // don't advance
                            standVotes++;
                        }
                    }
                }

                if (advanceVotes > standVotes && advanceVotes > retreatVotes)
                {
                    _log.Enqueue(squad.Name + " advances");
                    if (chargeVotes >= advanceVotes / 2)
                    {
                        foreach (BattleSoldier soldier in squad.Soldiers)
                        {
                            AddChargeActionsToBag(soldier);
                        }
                    }
                    else
                    {
                        foreach (BattleSoldier soldier in squad.Soldiers)
                        {
                            AddAdvanceActionsToBag(soldier);
                        }
                    }
                }
                else if (retreatVotes > standVotes && retreatVotes > advanceVotes)
                {
                    foreach (BattleSoldier soldier in squad.Soldiers)
                    {
                        AddRetreatingActionsToBag(soldier, squad);
                    }
                }
                else
                {
                    foreach (BattleSoldier soldier in squad.Soldiers)
                    {
                        AddStandingActionsToBag(soldier);
                    }
                }
            }
        }
        private void AddChargeActionsHelper(BattleSoldier soldier, int closestEnemyId, Tuple <int, int> currentPosition, float distance, BattleSquad oppSquad, Tuple <int, int> newPos)
        {
            Tuple <int, int> move       = new Tuple <int, int>(newPos.Item1 - currentPosition.Item1, newPos.Item2 - currentPosition.Item2);
            float            distanceSq = ((move.Item1 * move.Item1) + (move.Item2 * move.Item2));
            float            moveSpeed  = soldier.GetMoveSpeed();

            if (distance > moveSpeed + 1)
            {
                // we can't make it to an enemy in one move
                // soldier can't get there in one move, advance as far as possible

                Tuple <int, int> realMove = CalculateMovementAlongLine(move, moveSpeed);
                AddMoveAction(soldier, moveSpeed, realMove);

                // should the soldier shoot along the way?
                ShootIfReasonable(soldier, true);
            }
            else if (soldier.EquippedMeleeWeapons.Count == 0 && soldier.MeleeWeapons.Count > 0)
            {
                soldier.CurrentSpeed = 0;
                _shootActionBag.Add(new ReadyMeleeWeaponAction(soldier, soldier.MeleeWeapons[0]));
            }
            else
            {
                Debug.Log(soldier.Soldier.Name + " charging " + moveSpeed.ToString("F0"));
                soldier.CurrentSpeed = moveSpeed;
                _grid.ReserveSpace(newPos);
                _moveActionBag.Add(new MoveAction(soldier, _grid, newPos, _moveResolutionBag));
                BattleSoldier target = oppSquad.Soldiers.Single(s => s.Soldier.Id == closestEnemyId);
                _meleeActionBag.Add(new MeleeAttackAction(soldier, target, soldier.MeleeWeapons.Count == 0 ? null : soldier.EquippedMeleeWeapons[0], distance >= 2, _woundResolutionBag, _log));
            }
        }
        private void AddChargeActionsToBag(BattleSoldier soldier)
        {
            if (soldier.IsInMelee)
            {
                // determine what sort of manuver to make
                AddMeleeActionsToBag(soldier);
            }
            else
            {
                // get stuck in
                // move adjacent to nearest enemy
                // TODO: handle when someone else in the same squad wants to use the same spot
                // TODO: probably by letting the one with the lower id have it, and the higher id has to
                Tuple <int, int> currentPosition = _grid.GetSoldierPosition(soldier.Soldier.Id);
                float            distance        = _grid.GetNearestEnemy(soldier.Soldier.Id, out int closestEnemyId);
                float            moveSpeed       = soldier.GetMoveSpeed();
                Tuple <int, int> enemyPosition   = _grid.GetSoldierPosition(closestEnemyId);
                if (distance > moveSpeed + 1)
                {
                    Tuple <int, int> moveVector = new Tuple <int, int>(enemyPosition.Item1 - currentPosition.Item1, enemyPosition.Item2 - currentPosition.Item2);
                    // we can't make it to an enemy in one move
                    // soldier can't get there in one move, advance as far as possible
                    AddMoveAction(soldier, moveSpeed, moveVector);

                    // should the soldier shoot along the way?
                    ShootIfReasonable(soldier, true);
                }
                else
                {
                    Tuple <int, int> newPos   = _grid.GetClosestOpenAdjacency(currentPosition, enemyPosition);
                    BattleSquad      oppSquad = _opposingSoldierIdSquadMap[closestEnemyId];
                    if (newPos == null)
                    {
                        // find the next closest
                        // okay, this is one of those times where I made something because it made me feel smart,
                        // but it's probably unreadable so I should change it later
                        // basically, foreach soldier in the squad of the closest enemy, except the closest enemy (who we already checked)
                        // get their locations, and then sort it according to distance square
                        // PROTIP: SQRT is a relatively expensive operation, so sort by distance squares when it's about comparative, not absolute, distance
                        var map = oppSquad.Soldiers
                                  .Where(s => s.Soldier.Id != closestEnemyId)
                                  .Select(s => new Tuple <int, Tuple <int, int> >(s.Soldier.Id, _grid.GetSoldierPosition(s.Soldier.Id)))
                                  .Select(t => new Tuple <int, Tuple <int, int>, Tuple <int, int> >(t.Item1, t.Item2, new Tuple <int, int>(t.Item2.Item1 - currentPosition.Item1, t.Item2.Item2 - currentPosition.Item2)))
                                  .Select(u => new Tuple <int, Tuple <int, int>, int>(u.Item1, u.Item2, (u.Item3.Item1 * u.Item3.Item1 + u.Item3.Item2 * u.Item3.Item2)))
                                  .OrderBy(u => u.Item3);
                        foreach (Tuple <int, Tuple <int, int>, int> soldierData in map)
                        {
                            newPos = _grid.GetClosestOpenAdjacency(currentPosition, soldierData.Item2);
                            if (newPos != null)
                            {
                                AddChargeActionsHelper(soldier, soldierData.Item1, currentPosition, Mathf.Sqrt(soldierData.Item3), oppSquad, newPos);
                                break;
                            }
                        }
                        if (newPos == null)
                        {
                            // we weren't able to find an enemy to get near, guess we try to find someone to shoot, instead?
                            Debug.Log("ISoldier in squad engaged in melee couldn't find anyone to attack");
                            AddStandingActionsToBag(soldier);
                        }
                    }
                    else
                    {
                        AddChargeActionsHelper(soldier, closestEnemyId, currentPosition, distance, oppSquad, newPos);
                    }
                }
            }
        }