diff --git a/Assets/Scripts/ServerShared/Agents/States/Combat.cs b/Assets/Scripts/ServerShared/Agents/States/Combat.cs index 6feeed610..0450c50d7 100644 --- a/Assets/Scripts/ServerShared/Agents/States/Combat.cs +++ b/Assets/Scripts/ServerShared/Agents/States/Combat.cs @@ -33,6 +33,25 @@ public class CombatState : BaseState var toTarget = target.Position - _agent.Ship.Position; var targetDistance = length(toTarget); + + // Cut 2 (docs/fire-control-cut.md): aim at whichever revealed weapon on the target hits hardest at + // this range, or clear the aim point when nothing qualifies -- the same predicate + // (FireControl.IsRevealed) and the same writer (TrySelectTargetItem) the player path uses. + EquippedItem bestTargetItem = null; + var bestTargetDps = float.MinValue; + foreach (var targetItem in target.Equipment) + { + if (!(targetItem.Behaviors.FirstOrDefault(b => b is Weapon) is Weapon targetWeapon)) continue; + if (!FireControl.IsRevealed(_agent.Ship, targetItem)) continue; + var dps = targetWeapon.RangeDamagePerSecond(targetDistance); + if (dps > bestTargetDps) + { + bestTargetDps = dps; + bestTargetItem = targetItem; + } + } + _agent.Ship.TrySelectTargetItem(bestTargetItem); + for (var i = 0; i < _agent.Ship.WeaponGroups.Length; i++) { var group = _agent.Ship.WeaponGroups[i]; diff --git a/Assets/Scripts/ServerShared/LoadoutGenerator.cs b/Assets/Scripts/ServerShared/LoadoutGenerator.cs index da432b7c2..242854d20 100644 --- a/Assets/Scripts/ServerShared/LoadoutGenerator.cs +++ b/Assets/Scripts/ServerShared/LoadoutGenerator.cs @@ -258,6 +258,26 @@ public class LoadoutGenerator capacitor.Rotation = capacitorRotation; if (!entity.TryEquip(capacitor, capacitorPosition)) throw new InvalidLoadoutException("Failed to equip selected capacitor!"); + + // Cut 2 (docs/fire-control-cut.md, Q4): required equipment for anything that can fire -- unaided + // fire is a last resort, not an alternative loadout choice. EquipHardpoints runs before FillInterior + // (OutfitEntity), so entity.Weapons is already populated here. Zenith and anything else with no + // weapon hardpoints gets none. + if (entity.Weapons.Any()) + { + emptyShape = entity.UnoccupiedSpace; + + var (targetingProduct, targetingData) = RandomProduct(2, item => + item.Behaviors.Any(b => b is TargetingSystemData) && + item.Shape.FitsWithin(emptyShape, out _, out _), required: true); + if (targetingData == null) throw new InvalidLoadoutException("No compatible targeting system found for entity!"); + + targetingData.Shape.FitsWithin(emptyShape, out var targetingRotation, out var targetingPosition); + var targetingSystem = ItemManager.CreateInstance(targetingProduct) as EquippableItem; + targetingSystem.Rotation = targetingRotation; + if (!entity.TryEquip(targetingSystem, targetingPosition)) + throw new InvalidLoadoutException("Failed to equip selected targeting system!"); + } } }