Gate weapon fire on the shooter's own stance, shared by player and AI

Weapon.StanceAllowsFire is the one shared gate: true with no target, else the
shooter's own Entity.IsHostileTo(Target) -- not the target's stance toward the
shooter. It lives on the Weapon base class so every weapon subtype and every
caller (player input, TurretController, AI) goes through the same rule.

Wired at the two places weapons actually start dealing damage:
- InstantWeapon.Trigger (the base for InstantWeapon, AutoWeapon, ChargedWeapon
  and LockWeapon/Launcher, all of which route real firing through Trigger).
- ConstantWeapon.Execute's continuous-beam loop, which stops firing (and
  raises OnStopFiring) the moment the shooter's stance disallows it.

Also fixes LockWeapon's lock-building condition, which read the wrong
direction (target.IsHostileTo(shooter) instead of shooter.IsHostileTo(target))
-- the same backwards check as the HUD lock indicator, fixed alongside this in
ActionGameManager.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-17 14:00:59 +02:00
commit a351009956
4 changed files with 17 additions and 2 deletions

View file

@ -75,6 +75,13 @@ public class ConstantWeapon : Weapon, IProgressBehavior, IEventBehavior
public override bool Execute(float dt)
{
base.Execute(dt);
if (_firing && !StanceAllowsFire)
{
// Safed: shooter has a target and hasn't declared hostility toward it.
_firing = false;
OnStopFiring?.Invoke();
return false;
}
if (_firing)
{
if (!Entity.TryConsumeEnergy(Evaluate(_data.Energy) * dt))

View file

@ -90,6 +90,9 @@ public class InstantWeapon : Weapon, IProgressBehavior, IEventBehavior
protected void Trigger()
{
// Safed: shooter has a target and hasn't declared hostility toward it.
if (!StanceAllowsFire) return;
// If 1 ammo is consumed per burst, perform ammo and energy consumption here
// UseAmmo returns false when triggering reload; cancel firing if that is the case
if(_data.SingleAmmoBurst && (!Entity.TryConsumeEnergy(Energy) || !UseAmmo())) return;

View file

@ -77,7 +77,7 @@ public class LockWeapon : InstantWeapon
_target = Entity.Target.Value;
}
if (Entity.Target.Value != null && Entity.Target.Value.IsHostileTo(Entity))
if (Entity.Target.Value != null && Entity.IsHostileTo(Entity.Target.Value))
{
LockSpeed = Evaluate(_data.LockSpeed);
SensorImpact = Evaluate(_data.SensorImpact);

View file

@ -94,7 +94,12 @@ public abstract class Weapon : Behavior, IActivatedBehavior
{
get => _firing;
}
// Single shared fire gate for player and AI shooters alike: weapons are safed by the
// shooter's OWN declared stance toward its target, not the target's stance toward the shooter.
// With no target set, behaviour is unchanged (nothing to be safe about).
public bool StanceAllowsFire => Entity.Target.Value == null || Entity.IsHostileTo(Entity.Target.Value);
public Weapon(WeaponData data, EquippedItem item) : base(data, item)
{
_data = data;