From 6d459c406bcb6ae4262639b3e99dff1c6959f94a Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Mon, 15 Jun 2026 18:27:29 +0100 Subject: [PATCH] Stop weapon behaviors retaining configs --- .voidbot/state/aetheria.cc | Bin 121502 -> 122261 bytes Aetheria.State/README.md | 8 +- .../ServerShared/Behaviors/ChargedWeapon.cs | 69 +++++++++++++----- .../ServerShared/Behaviors/ConstantWeapon.cs | 28 +++---- .../ServerShared/Behaviors/InstantWeapon.cs | 41 +++++++---- .../ServerShared/Behaviors/LockWeapon.cs | 28 +++++-- .../Scripts/ServerShared/Behaviors/Weapon.cs | 63 ++++++++++++---- docs/aetheria-perfect-machine-map.md | 19 ++++- 8 files changed, 180 insertions(+), 76 deletions(-) diff --git a/.voidbot/state/aetheria.cc b/.voidbot/state/aetheria.cc index a31c53f7fab7e052b58663309a10e8dd62ec9d66..c41f927e13829dbaf89fc90f30d73e7d06f47bbc 100644 GIT binary patch delta 1930 zcmaJ?PiP%Q733@Cs#) z21zXuC@mvPa}kt3usp{7pU7->yGWnJO%~|Z@}xPub?q`)1G}}&0-H_=Yav^s>#*MT zFC@jgS{11k$wgSfALjewpaf4ao7zmXBCSQ-2AhUbybPF<2jUwAZVReI&q8NOUc@a=iH3%tcbBSNC zew5mR20~LGqp$_Q{}Q$v(3wD(@SYQ45_v{8cwjb?YcG)9BVRoYXUxSK7Hdw2Oev~d z5qUOMz@+7-*gRzbwZXy`(Ipo$Tm)Ls7hoSlk6D07r5HH8Mv+ZQVPzN`ahJl5a6d~p zSs5k!yG)76@z==E-ttE*uv{Uh!BIcoHT}kW5}fs^n7wqYG2fgWiZ5*Rtt2Oy!pJGH z4Y>?xTRVX*x0%ln@RVU0zvu$gixjPFnFrVkz6?d0bI$azNWpOsK_nVDth(vK;GR*a z#mRmDN6JS3k0ZMZS;~*ahn5~Fnx)gCb`f(8Y95q~ zN=n&r$78iDsdhoADTsexA5Fq9NN;ybp(gao+16@XY~#^%1SE1Pm5G@Gk&k#*+YsmK zfB<9-A#{OZri)zhD)+cXi*eJ2Hp?qVHkDM5DsKhlWHu~JW~Wc*i*1SJD$eJG!?+ho z!zc;J##H`>f)1C6%vP-@eFw-uJh?Q`WyX(*;Le)AZSK=yph5r}!9~p)Uiu*l(BzI# zWAjrr84a{IQqHq_HoeMMU=jHrSdcMwD!%#neB;uu7w$Bq(MQ&wR@mlA-t#|Oygn0*()$hocEz{Vesp-=9 dB-%8Mr>4ppdA((-RE(+l!x~xb*|d$%{R@l7#(@9; delta 1454 zcmZ`(&2Jk;6lao_q=~(Bp24$zF2jhZm!9WHr90S8tk~j zS0CDCF2)~hoIUpE@koMKMsKO2gtVY^LD-Rk@~a?xlvda(glZXmuxf>CdQ>a)v4mBV zffLgGu4wfVdR8b2QmrHlU2M1jMWx}ilGxtRNM12zWz8X2aAS^QiF?(YKLmNMY1Ym+ zmp(jB7+Mve|KO>x!mTKXfTj+|r+yl&ed+~Vgx6W;lQd381UE$YY-41GnlROhG+;HC zCZH~|1cCB+(2j5JMD7U#q=IU)q)x-ea!Wn2hy>BYlsOcT_#x~kNTIL>2s~<-jRHH% zNz4qy+dmH0fXGIHPXf8!ZOVb;r({qCAGKIUC~6f#kjeo?fq`#Q0hQZ~(syW^qcWdc zl!mn4ywOnKh%j=5NPR+%TJpevu_6d+5E+EINciA*IY$=BkxZ&R$&wGaM4OYukcd%% z8``gEc>x1U-^SxoI^bNF&=nl)P)G;IDiu%yo-GRlUSvsdE=x!Pms10r5sx-8LqSE- z=ejyX`LOAV7E)+OzY{kfgM@`_Z*B9pSw*JG2%(VX(2dq;RHVs@Cccp-qym=tvLKcX zyfl_V?3hzY5w0{L|Mrs*x7>D_K1BXvr4_a0yBzDQ;i*a>JYiG%zDh}GBnn#j5y7}A z%Of|%c0~)4)Mbi9(k|owIWw6rNd2O_)%hwYB#TaaA6%x+El+;^nWr+G5| zzfpYVS~=apX5)*8_b0_O?A~-j{dr*6o4h?qx8K0w@%mPVP4&M!GmvZ?VGrE(@bviP z^u!~XgW2rjCzh8;Usdm!VSnvjJ^wYkzy8!5gEy1;3#=#k^&0Do*E;WM-k;g=c)N37 w^6eV?VNc^m%}$IZul~Z$wXV~Nth;iP1MBRyRw+B`O7$!2?DC#}YT3oV0WjqgoB#j- diff --git a/Aetheria.State/README.md b/Aetheria.State/README.md index 6a5608217..a65d0298b 100644 --- a/Aetheria.State/README.md +++ b/Aetheria.State/README.md @@ -192,8 +192,12 @@ Reactor, Radiator, Sensor, and StatModifier now copy their constructor performance stats, curves, flags, and target strings into explicit runtime fields; those live behaviors no longer retain their config subclasses. AetherDrive now follows the same rule for rotor geometry, performance stats, -torque curve, audio parameters, and prefab path. Remaining runtime config -retention is concentrated in the weapon family. +torque curve, audio parameters, and prefab path. Weapon, InstantWeapon, +ConstantWeapon, ChargedWeapon, and LockWeapon now copy constructor stats, +curves, ammo/reload fields, guided projectile profile fields, burst/cooldown +fields, charge multipliers, and lock parameters into runtime-owned fields. +Behavior instances no longer retain their `RuntimeBehaviorConfig` subclasses +after construction. `ItemManager` currently owns the explicit typed-payload-to-config bridge. Typed name-file documents are `aetheria.name_file.v2` records. They carry the legacy ID, display name, count, compact sample names for surfaces, and the full diff --git a/Assets/Scripts/ServerShared/Behaviors/ChargedWeapon.cs b/Assets/Scripts/ServerShared/Behaviors/ChargedWeapon.cs index 17206f604..a3872a02a 100644 --- a/Assets/Scripts/ServerShared/Behaviors/ChargedWeapon.cs +++ b/Assets/Scripts/ServerShared/Behaviors/ChargedWeapon.cs @@ -56,7 +56,18 @@ public class ChargedWeaponConfig : InstantWeaponConfig public class ChargedWeapon : InstantWeapon { - private ChargedWeaponConfig _data; + private readonly PerformanceStat _chargeTime; + private readonly PerformanceStat _chargeEnergy; + private readonly PerformanceStat _chargeHeat; + private readonly bool _canFireEarly; + private readonly float _failureCharge; + private readonly float _failureDamage; + private readonly float _chargeFiringDamageMultiplier; + private readonly float _chargeFiringSpreadMultiplier; + private readonly float _chargeFiringBurstCountMultiplier; + private readonly float _chargeFiringVisibilityMultiplier; + private readonly float _chargeFiringVelocityMultiplier; + private readonly float _chargeFiringHeatMultiplier; private bool _charging; private bool _charged; private float _charge; @@ -66,12 +77,12 @@ public class ChargedWeapon : InstantWeapon public float ChargeEnergy { get; protected set; } public float ChargeHeat { get; protected set; } - public override float DamagePerSecond => Damage * _data.ChargeFiringDamageMultiplier / (Cooldown + ChargeTime); + public override float DamagePerSecond => Damage * _chargeFiringDamageMultiplier / (Cooldown + ChargeTime); public override float RangeDamagePerSecond(float range) { return Damage * - _data.ChargeFiringDamageMultiplier * - _data.DamageCurve.Evaluate(saturate(unlerp(MinRange, Range, range))) / + _chargeFiringDamageMultiplier * + DamageCurve.Evaluate(saturate(unlerp(MinRange, Range, range))) / (Cooldown + ChargeTime); } @@ -100,26 +111,48 @@ public class ChargedWeapon : InstantWeapon public ChargedWeapon(ChargedWeaponConfig data, EquippedItem item) : base(data, item) { - _data = data; + _chargeTime = data.ChargeTime; + _chargeEnergy = data.ChargeEnergy; + _chargeHeat = data.ChargeHeat; + _canFireEarly = data.CanFireEarly; + _failureCharge = data.FailureCharge; + _failureDamage = data.FailureDamage; + _chargeFiringDamageMultiplier = data.ChargeFiringDamageMultiplier; + _chargeFiringSpreadMultiplier = data.ChargeFiringSpreadMultiplier; + _chargeFiringBurstCountMultiplier = data.ChargeFiringBurstCountMultiplier; + _chargeFiringVisibilityMultiplier = data.ChargeFiringVisibilityMultiplier; + _chargeFiringVelocityMultiplier = data.ChargeFiringVelocityMultiplier; + _chargeFiringHeatMultiplier = data.ChargeFiringHeatMultiplier; } public ChargedWeapon(ChargedWeaponConfig data, ConsumableItemEffect item) : base(data, item) { - _data = data; + _chargeTime = data.ChargeTime; + _chargeEnergy = data.ChargeEnergy; + _chargeHeat = data.ChargeHeat; + _canFireEarly = data.CanFireEarly; + _failureCharge = data.FailureCharge; + _failureDamage = data.FailureDamage; + _chargeFiringDamageMultiplier = data.ChargeFiringDamageMultiplier; + _chargeFiringSpreadMultiplier = data.ChargeFiringSpreadMultiplier; + _chargeFiringBurstCountMultiplier = data.ChargeFiringBurstCountMultiplier; + _chargeFiringVisibilityMultiplier = data.ChargeFiringVisibilityMultiplier; + _chargeFiringVelocityMultiplier = data.ChargeFiringVelocityMultiplier; + _chargeFiringHeatMultiplier = data.ChargeFiringHeatMultiplier; } protected override void UpdateStats() { base.UpdateStats(); - ChargeTime = Evaluate(_data.ChargeTime); - ChargeEnergy = Evaluate(_data.ChargeEnergy); - ChargeHeat = Evaluate(_data.ChargeHeat); - Damage *= lerp(1, _data.ChargeFiringDamageMultiplier, saturate(_charge)); - Heat *= lerp(1, _data.ChargeFiringHeatMultiplier, saturate(_charge)); - Spread *= lerp(1, _data.ChargeFiringSpreadMultiplier, saturate(_charge)); - BurstCount *= lerp(1, _data.ChargeFiringBurstCountMultiplier, saturate(_charge)); - Visibility *= lerp(1, _data.ChargeFiringVisibilityMultiplier, saturate(_charge)); - Velocity *= lerp(1, _data.ChargeFiringVelocityMultiplier, saturate(_charge)); + ChargeTime = Evaluate(_chargeTime); + ChargeEnergy = Evaluate(_chargeEnergy); + ChargeHeat = Evaluate(_chargeHeat); + Damage *= lerp(1, _chargeFiringDamageMultiplier, saturate(_charge)); + Heat *= lerp(1, _chargeFiringHeatMultiplier, saturate(_charge)); + Spread *= lerp(1, _chargeFiringSpreadMultiplier, saturate(_charge)); + BurstCount *= lerp(1, _chargeFiringBurstCountMultiplier, saturate(_charge)); + Visibility *= lerp(1, _chargeFiringVisibilityMultiplier, saturate(_charge)); + Velocity *= lerp(1, _chargeFiringVelocityMultiplier, saturate(_charge)); } public override bool Execute(float dt) @@ -137,7 +170,7 @@ public class ChargedWeapon : InstantWeapon OnCharged?.Invoke(); } } - if (_data.FailureCharge > 1 && _charge > _data.FailureCharge) + if (_failureCharge > 1 && _charge > _failureCharge) { _charging = false; _cooldown = 1; @@ -145,7 +178,7 @@ public class ChargedWeapon : InstantWeapon _charge = 0; OnFailed?.Invoke(); Item.FireAudioEvent(ChargedWeaponAudioEvent.Fail); - CauseDamage(_data.FailureDamage); + CauseDamage(_failureDamage); } } return base.Execute(dt); @@ -166,7 +199,7 @@ public class ChargedWeapon : InstantWeapon { if (_charging) { - if (_data.CanFireEarly || _charge > 1) + if (_canFireEarly || _charge > 1) { Trigger(); _charge = 0; diff --git a/Assets/Scripts/ServerShared/Behaviors/ConstantWeapon.cs b/Assets/Scripts/ServerShared/Behaviors/ConstantWeapon.cs index 14c256e18..fb4a8e489 100644 --- a/Assets/Scripts/ServerShared/Behaviors/ConstantWeapon.cs +++ b/Assets/Scripts/ServerShared/Behaviors/ConstantWeapon.cs @@ -25,7 +25,7 @@ public class ConstantWeaponConfig : WeaponConfig public class ConstantWeapon : Weapon, IProgressBehavior, IEventBehavior { - private ConstantWeaponConfig _data; + private readonly float _ammoIntervalDuration; private int _ammo = 1; private float _ammoInterval; private float _reload; @@ -47,7 +47,7 @@ public class ConstantWeapon : Weapon, IProgressBehavior, IEventBehavior public override float DamagePerSecond => Damage; public override float RangeDamagePerSecond(float range) { - return Damage * _data.DamageCurve.Evaluate(saturate(unlerp(MinRange, Range, range))); + return Damage * DamageCurve.Evaluate(saturate(unlerp(MinRange, Range, range))); } public event Action OnReloadBegin; @@ -65,12 +65,12 @@ public class ConstantWeapon : Weapon, IProgressBehavior, IEventBehavior public ConstantWeapon(ConstantWeaponConfig data, EquippedItem item) : base(data, item) { - _data = data; + _ammoIntervalDuration = data.AmmoInterval; } public ConstantWeapon(ConstantWeaponConfig data, ConsumableItemEffect item) : base(data, item) { - _data = data; + _ammoIntervalDuration = data.AmmoInterval; } public override bool Execute(float dt) @@ -78,17 +78,17 @@ public class ConstantWeapon : Weapon, IProgressBehavior, IEventBehavior base.Execute(dt); if (_firing) { - if (!Entity.TryConsumeEnergy(Evaluate(_data.Energy) * dt)) + if (!Entity.TryConsumeEnergy(Energy * dt)) { _firing = false; OnStopFiring?.Invoke(); return false; } - if (!string.IsNullOrWhiteSpace(_data.AmmoItemKey)) + if (!string.IsNullOrWhiteSpace(AmmoItemKey)) { if (_reloading) { - _reload -= dt / _data.ReloadTime; + _reload -= dt / ReloadTime; if (_reload < 0) { _reloading = false; @@ -97,21 +97,21 @@ public class ConstantWeapon : Weapon, IProgressBehavior, IEventBehavior return false; } - _ammoInterval -= dt / _data.AmmoInterval; + _ammoInterval -= dt / _ammoIntervalDuration; if (_ammoInterval < 0) { _ammoInterval = 1; - if (_data.MagazineSize > 1 && _ammo > 0) _ammo--; + if (MagazineSize > 1 && _ammo > 0) _ammo--; else { - var cargo = Entity.FindItemInCargo(_data.AmmoItemKey); + var cargo = Entity.FindItemInCargo(AmmoItemKey); if (cargo != null) { - var item = cargo.GetFirstItem(_data.AmmoItemKey); + var item = cargo.GetFirstItem(AmmoItemKey); if (item is SimpleCommodity simpleCommodity) cargo.Remove(simpleCommodity, 1); - if(_data.MagazineSize > 1) + if(MagazineSize > 1) { _reloading = true; _reload = 1; @@ -127,8 +127,8 @@ public class ConstantWeapon : Weapon, IProgressBehavior, IEventBehavior } CauseWearDamage(dt); - AddHeat(Evaluate(_data.Heat) * dt); - Entity.VisibilitySources[this] = Evaluate(_data.Visibility); + AddHeat(Heat * dt); + Entity.VisibilitySources[this] = Visibility; } return true; } diff --git a/Assets/Scripts/ServerShared/Behaviors/InstantWeapon.cs b/Assets/Scripts/ServerShared/Behaviors/InstantWeapon.cs index f36cc0032..df39a4122 100644 --- a/Assets/Scripts/ServerShared/Behaviors/InstantWeapon.cs +++ b/Assets/Scripts/ServerShared/Behaviors/InstantWeapon.cs @@ -32,7 +32,10 @@ public class InstantWeaponConfig : WeaponConfig public class InstantWeapon : Weapon, IProgressBehavior, IEventBehavior { - private InstantWeaponConfig _data; + private readonly PerformanceStat _count; + private readonly PerformanceStat _burstTime; + private readonly PerformanceStat _cooldownDuration; + private readonly bool _singleAmmoBurst; protected int _burstRemaining; private float _burstTimer; @@ -52,7 +55,7 @@ public class InstantWeapon : Weapon, IProgressBehavior, IEventBehavior public override float DamagePerSecond => Damage / Cooldown; public override float RangeDamagePerSecond(float range) { - return Damage * _data.DamageCurve.Evaluate(saturate(unlerp(MinRange, Range, range))) / Cooldown; + return Damage * DamageCurve.Evaluate(saturate(unlerp(MinRange, Range, range))) / Cooldown; } public override int Ammo @@ -81,13 +84,19 @@ public class InstantWeapon : Weapon, IProgressBehavior, IEventBehavior public InstantWeapon(InstantWeaponConfig data, EquippedItem item) : base(data, item) { - _data = data; + _count = data.Count; + _burstTime = data.BurstTime; + _cooldownDuration = data.Cooldown; + _singleAmmoBurst = data.SingleAmmoBurst; _ammo = data.MagazineSize; } public InstantWeapon(InstantWeaponConfig data, ConsumableItemEffect item) : base(data, item) { - _data = data; + _count = data.Count; + _burstTime = data.BurstTime; + _cooldownDuration = data.Cooldown; + _singleAmmoBurst = data.SingleAmmoBurst; _ammo = data.MagazineSize; } @@ -95,7 +104,7 @@ public class InstantWeapon : Weapon, IProgressBehavior, IEventBehavior { // 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; + if(_singleAmmoBurst && (!Entity.TryConsumeEnergy(Energy) || !UseAmmo())) return; _burstRemaining = (int) BurstCount; _burstInterval = BurstTime / _burstRemaining; @@ -107,9 +116,9 @@ public class InstantWeapon : Weapon, IProgressBehavior, IEventBehavior protected override void UpdateStats() { base.UpdateStats(); - BurstCount = Evaluate(_data.Count); - BurstTime = Evaluate(_data.BurstTime); - Cooldown = Evaluate(_data.Cooldown); + BurstCount = Evaluate(_count); + BurstTime = Evaluate(_burstTime); + Cooldown = Evaluate(_cooldownDuration); Damage /= (int) BurstCount; Heat /= (int) BurstCount; @@ -118,7 +127,7 @@ public class InstantWeapon : Weapon, IProgressBehavior, IEventBehavior private bool UseAmmo() { - if (_data.MagazineSize <= 1) return true; + if (MagazineSize <= 1) return true; if (_ammo > 0) { @@ -127,12 +136,12 @@ public class InstantWeapon : Weapon, IProgressBehavior, IEventBehavior } var hasAmmo = true; - if (!string.IsNullOrWhiteSpace(_data.AmmoItemKey)) + if (!string.IsNullOrWhiteSpace(AmmoItemKey)) { - var cargo = Entity.FindItemInCargo(_data.AmmoItemKey); + var cargo = Entity.FindItemInCargo(AmmoItemKey); if (cargo != null) { - var item = cargo.GetFirstItem(_data.AmmoItemKey); + var item = cargo.GetFirstItem(AmmoItemKey); if (item is SimpleCommodity simpleCommodity) cargo.Remove(simpleCommodity, 1); } @@ -155,13 +164,13 @@ public class InstantWeapon : Weapon, IProgressBehavior, IEventBehavior base.Execute(dt); if (_coolingDown) { - _cooldown -= dt / (_data.MagazineSize > 0 && _ammo == 0 ? _data.ReloadTime : Cooldown); + _cooldown -= dt / (MagazineSize > 0 && _ammo == 0 ? ReloadTime : Cooldown); if (_cooldown < 0) { _coolingDown = false; - if (_data.MagazineSize > 0 && _ammo == 0) + if (MagazineSize > 0 && _ammo == 0) { - _ammo = _data.MagazineSize; + _ammo = MagazineSize; OnReloadComplete?.Invoke(); } else @@ -175,7 +184,7 @@ public class InstantWeapon : Weapon, IProgressBehavior, IEventBehavior { // If multiple 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())) + if (!_singleAmmoBurst && (!Entity.TryConsumeEnergy(Energy) || !UseAmmo())) { _burstRemaining = 0; return false; diff --git a/Assets/Scripts/ServerShared/Behaviors/LockWeapon.cs b/Assets/Scripts/ServerShared/Behaviors/LockWeapon.cs index 45225b99a..dac1dcf58 100644 --- a/Assets/Scripts/ServerShared/Behaviors/LockWeapon.cs +++ b/Assets/Scripts/ServerShared/Behaviors/LockWeapon.cs @@ -34,7 +34,11 @@ public class LockWeaponConfig : InstantWeaponConfig public class LockWeapon : InstantWeapon { - private LockWeaponConfig _data; + private readonly PerformanceStat _lockSpeed; + private readonly PerformanceStat _sensorImpact; + private readonly PerformanceStat _lockAngle; + private readonly PerformanceStat _directionImpact; + private readonly PerformanceStat _decay; private float _lock; private bool _locking; private Entity _target; @@ -64,11 +68,19 @@ public class LockWeapon : InstantWeapon public LockWeapon(LockWeaponConfig data, EquippedItem item) : base(data, item) { - _data = data; + _lockSpeed = data.LockSpeed; + _sensorImpact = data.SensorImpact; + _lockAngle = data.LockAngle; + _directionImpact = data.DirectionImpact; + _decay = data.Decay; } public LockWeapon(LockWeaponConfig data, ConsumableItemEffect item) : base(data, item) { - _data = data; + _lockSpeed = data.LockSpeed; + _sensorImpact = data.SensorImpact; + _lockAngle = data.LockAngle; + _directionImpact = data.DirectionImpact; + _decay = data.Decay; } public override bool Execute(float dt) @@ -81,11 +93,11 @@ public class LockWeapon : InstantWeapon if (Entity.Target.Value != null && Entity.Target.Value.IsHostileTo(Entity)) { - LockSpeed = Evaluate(_data.LockSpeed); - SensorImpact = Evaluate(_data.SensorImpact); - LockAngle = Evaluate(_data.LockAngle); - DirectionImpact = Evaluate(_data.DirectionImpact); - Decay = Evaluate(_data.Decay); + LockSpeed = Evaluate(_lockSpeed); + SensorImpact = Evaluate(_sensorImpact); + LockAngle = Evaluate(_lockAngle); + DirectionImpact = Evaluate(_directionImpact); + Decay = Evaluate(_decay); var degrees = acos(dot(normalize(Entity.Target.Value.Position - Entity.Position), normalize(Entity.LookDirection))) * 57.2958f; if (degrees < LockAngle) diff --git a/Assets/Scripts/ServerShared/Behaviors/Weapon.cs b/Assets/Scripts/ServerShared/Behaviors/Weapon.cs index 234f34d1a..e0c28ba5d 100644 --- a/Assets/Scripts/ServerShared/Behaviors/Weapon.cs +++ b/Assets/Scripts/ServerShared/Behaviors/Weapon.cs @@ -59,7 +59,16 @@ public abstract class WeaponConfig : RuntimeBehaviorConfig public abstract class Weapon : Behavior, IActivatedBehavior { - private WeaponConfig _data; + private readonly PerformanceStat _damage; + private readonly PerformanceStat _penetration; + private readonly PerformanceStat _damageSpread; + private readonly PerformanceStat _minRange; + private readonly PerformanceStat _range; + private readonly PerformanceStat _energy; + private readonly PerformanceStat _heat; + private readonly PerformanceStat _visibility; + private readonly PerformanceStat _spread; + private readonly PerformanceStat _velocity; private PerformanceStat _guidedProjectileThrust; private PerformanceStat _guidedProjectileVelocity; @@ -70,6 +79,8 @@ public abstract class Weapon : Behavior, IActivatedBehavior public string EffectPrefab { get; } public string AmmoItemKey { get; } public int MagazineSize { get; } + protected float ReloadTime { get; } + protected BezierCurve DamageCurve { get; } public bool UsesAmmo => !string.IsNullOrWhiteSpace(AmmoItemKey); public GuidedProjectileTargetMode GuidedProjectileTargeting { get; private set; } public bool HasGuidedProjectileProfile => GuidedProjectileTargeting != GuidedProjectileTargetMode.None; @@ -98,21 +109,43 @@ public abstract class Weapon : Behavior, IActivatedBehavior public Weapon(WeaponConfig data, EquippedItem item) : base(data, item) { - _data = data; + _damage = data.Damage; + _penetration = data.Penetration; + _damageSpread = data.DamageSpread; + _minRange = data.MinRange; + _range = data.Range; + _energy = data.Energy; + _heat = data.Heat; + _visibility = data.Visibility; + _spread = data.Spread; + _velocity = data.Velocity; DamageType = data.DamageType; + DamageCurve = data.DamageCurve; EffectPrefab = data.EffectPrefab ?? ""; AmmoItemKey = data.AmmoItemKey; MagazineSize = data.MagazineSize; + ReloadTime = data.ReloadTime; InitializeGuidedProjectileProfile(data); } public Weapon(WeaponConfig data, ConsumableItemEffect item) : base(data, item) { - _data = data; + _damage = data.Damage; + _penetration = data.Penetration; + _damageSpread = data.DamageSpread; + _minRange = data.MinRange; + _range = data.Range; + _energy = data.Energy; + _heat = data.Heat; + _visibility = data.Visibility; + _spread = data.Spread; + _velocity = data.Velocity; DamageType = data.DamageType; + DamageCurve = data.DamageCurve; EffectPrefab = data.EffectPrefab ?? ""; AmmoItemKey = data.AmmoItemKey; MagazineSize = data.MagazineSize; + ReloadTime = data.ReloadTime; InitializeGuidedProjectileProfile(data); } @@ -142,16 +175,16 @@ public abstract class Weapon : Behavior, IActivatedBehavior protected virtual void UpdateStats() { - Damage = Evaluate(_data.Damage); - Penetration = Evaluate(_data.Penetration); - DamageSpread = Evaluate(_data.DamageSpread); - MinRange = Evaluate(_data.MinRange); - Range = Evaluate(_data.Range); - Energy = Evaluate(_data.Energy); - Heat = Evaluate(_data.Heat); - Visibility = Evaluate(_data.Visibility); - Spread = Evaluate(_data.Spread); - Velocity = Evaluate(_data.Velocity); + Damage = Evaluate(_damage); + Penetration = Evaluate(_penetration); + DamageSpread = Evaluate(_damageSpread); + MinRange = Evaluate(_minRange); + Range = Evaluate(_range); + Energy = Evaluate(_energy); + Heat = Evaluate(_heat); + Visibility = Evaluate(_visibility); + Spread = Evaluate(_spread); + Velocity = Evaluate(_velocity); } public override bool Execute(float dt) @@ -172,12 +205,12 @@ public abstract class Weapon : Behavior, IActivatedBehavior public float EvaluateRange() { - return Evaluate(_data.Range); + return Evaluate(_range); } public float EvaluateVelocity() { - return Evaluate(_data.Velocity); + return Evaluate(_velocity); } public float EvaluateGuidedProjectileThrust() diff --git a/docs/aetheria-perfect-machine-map.md b/docs/aetheria-perfect-machine-map.md index d5aa8ee73..436f6b8d1 100644 --- a/docs/aetheria-perfect-machine-map.md +++ b/docs/aetheria-perfect-machine-map.md @@ -316,8 +316,12 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted. stats, curves, flags, and target strings into explicit runtime fields instead of retaining config subclasses. AetherDrive now copies rotor geometry, performance stats, torque curve, audio - parameters, and prefab path into explicit runtime fields; remaining runtime - config retention is concentrated in the weapon family. + parameters, and prefab path into explicit runtime fields. Weapon, + InstantWeapon, ConstantWeapon, ChargedWeapon, and LockWeapon now copy + constructor stats, curves, ammo/reload fields, guided projectile profile + fields, burst/cooldown fields, charge multipliers, and lock parameters into + runtime-owned fields. Behavior instances no longer retain their + `RuntimeBehaviorConfig` subclasses after construction. `StatModifier` behavior requirements and behavior-stat targets compare typed behavior kinds on the live equipped behavior instances through `AetheriaRuntimeBehaviorMetadataCatalog` instead of scanning config @@ -1061,7 +1065,7 @@ First Aetheria surfaces to publish: behavior grouping use runtime instance identity. - Done: move TurretController's weapon range/velocity reads off weapon config casts and onto `Weapon.EvaluateRange`/`EvaluateVelocity`; - weapon config fields remain private inputs to the weapon runtime owner. + copied weapon range and velocity fields are owned by the weapon runtime. - Done: move projectile, hitscan, beam, lightning, and mine effect damage type reads off weapon configs and onto `Weapon.DamageType`; effect managers consume live weapon metadata instead of config DTO fields. @@ -1082,6 +1086,11 @@ First Aetheria surfaces to publish: - Done: move HUD ammo display reads off `WeaponData`; `SchematicDisplay` consumes `Weapon.AmmoItemKey`, `Weapon.MagazineSize`, and `Weapon.UsesAmmo`, and the public `WeaponData` escape hatch has been removed. + - Done: stop `Weapon`, `InstantWeapon`, `ConstantWeapon`, `ChargedWeapon`, + and `LockWeapon` from retaining runtime config subclasses; weapon-family + constructors now copy stats, curves, ammo/reload data, guided projectile + profile fields, burst/cooldown fields, charge multipliers, and lock + parameters into runtime-owned fields. - Done: delete dormant legacy item audio reads in `EntityInstance` and stale commented thruster sound-trigger code; those paths had no live output and therefore earned deletion rather than a new runtime metadata owner. @@ -1437,6 +1446,10 @@ First Aetheria surfaces to publish: exposed construction config objects. - Done: replace `ItemManager` behavior config reflection with an explicit typed behavior-kind mapper. + - Done: remove runtime config retention from the remaining weapon family; + all behavior subclasses now copy constructor config inputs into explicit + runtime fields instead of keeping `RuntimeBehaviorConfig` subclass + instances alive as state. - Done: delete `LegacyPayloadKeyAttribute` from live Unity source after importer constants and explicit typed mappers became the only migrated field-key authorities.