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)); } }
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); } } } }