private static IMoveTarget GetNextViking(Vector3 pos, float range) { // Refer to SiegeMonster::ClosestMonster and UnitSystem::GetClosestDamageable. float rangeSquared = range * range; float currentClosestDistance = float.MaxValue; int currentLowestAssigned = int.MaxValue; IMoveTarget nextViking = null; foreach (IMoveTarget viking in enemyAssignments.Keys) { UnitSystem.Army army = viking as UnitSystem.Army; SiegeMonster ogre = viking as SiegeMonster; if ((army == null && ogre == null) || (army != null && (army.IsInvalid() || !OnSameLandmass(pos, army.GetPos()))) || (ogre != null && (ogre.IsInvalid() || !OnSameLandmass(pos, ogre.GetPos()))) || !TargetTypeEnabled(viking)) { continue; } // Select the closest viking squad or ogre with the least points to obey the assignment and distribution // rules. int assigned = enemyAssignments[viking]; float distanceSquared = Mathff.DistSqrdXZ(pos, viking.GetPos()); if (distanceSquared > rangeSquared || assigned > currentLowestAssigned) { continue; } if (nextViking == null || assigned < currentLowestAssigned || (distanceSquared < currentClosestDistance && assigned == currentLowestAssigned)) { nextViking = viking; currentClosestDistance = distanceSquared; currentLowestAssigned = assigned; } } return(nextViking); }
// Takes into account if move target is a Transport Ship. Since Transport Ship is not IProjectileHitable, this // returns the ShipBase, which is IProjectileHitable. private static System.Object GetAttackTarget(UnitSystem.Army army, ArcherGeneral archerGeneral) { if (army.locked && army.moveTarget != null) { System.Object moveTarget = army.moveTarget; Vector3 targetPos; // Refer to ProjectileDefense::GetTarget for getting target position. if (moveTarget is TroopTransportShip) { // Prevents friendly fire. if (((TroopTransportShip)moveTarget).TeamID() != 0) { ShipBase shipBase = ((TroopTransportShip)moveTarget).shipBase; moveTarget = shipBase; targetPos = shipBase.GetPos(); } else { return(null); } } else if (moveTarget is SiegeMonster) { SiegeMonster ogre = (SiegeMonster)moveTarget; if (ogre.IsInvalid()) { // Ogre died or got on a ship, stop tracking. army.moveTarget = null; return(null); } targetPos = ogre.GetPos(); } else if (moveTarget is IProjectileHitable) { if (moveTarget is UnitSystem.Army && ((UnitSystem.Army)moveTarget).IsInvalid()) { // Army died or got on a ship, stop tracking. army.moveTarget = null; return(null); } targetPos = ((IProjectileHitable)moveTarget).GetPosition(); } else { return(null); } // If moveTarget is not in range, returning null will make archer seek another target until moveTarget // is in range. This is because moveTarget will not change until it is dead, or the player clicked on a // different target. // Refer to ArcherGeneral::Update and ProjectileDefense::GetTarget for range calculation. Vector3 archerPos = army.GetPos(); float attackRange = archerGeneral.FullRange(archerGeneral.attackRange) + 1f; if (!ProjectileDefense.TestRange(targetPos, archerPos, attackRange)) { return(null); } return(moveTarget); } else { // If not on ship, don't change the behavior. return(army.moveTarget); } }
static void Postfix(TroopTransportShip __instance, IMoveTarget target, List <IMoveableUnit> ___loadTarget) { if (__instance.TeamID() != 0 || target == null || target.TeamID() == 0 || target.TeamID() == -1) { // Not an allied ship, no target, target is allied, or target is environment. // Refer to UnitSystem::UpdatePathing for determining if target is an enemy. return; } // Check if ship has archers. bool shipHasArchers = false; // Refer to TroopTransportShip::UpdateArmyPosition. for (int i = 0; i < ___loadTarget.Count(); i++) { UnitSystem.Army army = ___loadTarget[i] as UnitSystem.Army; if (army != null) { bool isArcher = army.armyType == UnitSystem.ArmyType.Archer; if (isArcher) { shipHasArchers = true; break; } } } if (shipHasArchers) { IMoveTarget archersTarget = null; if (target is SiegeMonster) { SiegeMonster ogre = (SiegeMonster)target; bool ogreTargettable = !ogre.IsInvalid(); bool ogreDead = ogre.IsDead(); if (ogreTargettable) { // Ogre is on land. archersTarget = target; } else if (!ogreTargettable && !ogreDead) { // Ogre is on a ship. Assumption is that the closest ship is the ship it's on. Vector3 pos = ogre.GetPos(); // Refer to ProjectileDefense::GetTarget IProjectileHitable closestShip = ShipSystem.inst.GetClosestShipToAttack(pos, 1, 1f); ShipBase shipBase = closestShip as ShipBase; if (shipBase != null) { archersTarget = shipBase.GetComponentInParent <TroopTransportShip>(); } } } else if (target is TroopTransportShip || target is IProjectileHitable) { archersTarget = target; } // Set archer target. if (archersTarget != null) { for (int i = 0; i < ___loadTarget.Count(); i++) { UnitSystem.Army army = ___loadTarget[i] as UnitSystem.Army; if (army != null) { bool isArcher = army.armyType == UnitSystem.ArmyType.Archer; if (isArcher) { army.moveTarget = archersTarget; } } } } } }