using System; namespace ProjectI.Combat { public readonly struct GunTriggerBonus { public readonly float DamageBonus; public readonly bool Ultimate; public readonly float PierceFraction; public readonly int AdditionalPierceTargets; public GunTriggerBonus(float bonus,bool ultimate,float pierce,int additionalPierceTargets=0) {DamageBonus=bonus;Ultimate=ultimate;PierceFraction=pierce;AdditionalPierceTargets=additionalPierceTargets;} } public sealed class GunTriggerWindow { private sealed class Window { public AttackEnhancementTuning Tuning; public int Remaining; public double Until; public bool Granted; public EffectSourceKey Source; public bool Active(double now)=>Granted&&Finite(now)&&now0); public void Grant(double now,AttackEnhancementTuning tuning,double sourceUntil) { if(!Finite(now)||double.IsNaN(sourceUntil)||sourceUntil<=now)throw new ArgumentOutOfRangeException(nameof(now)); Tuning=tuning;Remaining=tuning.TriggerLimit;Until=tuning.DurationSeconds>0?Math.Min(sourceUntil,now+tuning.DurationSeconds):sourceUntil; if(double.IsInfinity(Until)&&tuning.DurationSeconds>0)throw new ArgumentOutOfRangeException(nameof(tuning)); Granted=true; } public void Clear(){Granted=false;Remaining=0;Until=0;} } private readonly Window focus=new Window(),rail=new Window(); public int FocusRemaining(double now)=>focus.Active(now)?(focus.Tuning.TriggerLimit==0?-1:focus.Remaining):0; public int RailRemaining(double now)=>rail.Active(now)?(rail.Tuning.TriggerLimit==0?-1:rail.Remaining):0; public double FocusUntil=>focus.Until; public double RailUntil=>rail.Until; public void Focus(double now)=>Focus(now,AttackEnhancementTuning.FocusDefault); public void Rail(double now)=>Rail(now,AttackEnhancementTuning.RailDefault); public void Focus(double now,AttackEnhancementTuning tuning)=>Focus(now,tuning,double.PositiveInfinity); public void Rail(double now,AttackEnhancementTuning tuning)=>Rail(now,tuning,double.PositiveInfinity); // Infinite source lifetime is explicit: source removal and owner cleanup retire it. public void Focus(double now,AttackEnhancementTuning tuning,double sourceUntil)=>focus.Grant(now,tuning,sourceUntil); public void Rail(double now,AttackEnhancementTuning tuning,double sourceUntil)=>rail.Grant(now,tuning,sourceUntil); public void Focus(double now,AttackEnhancementTuning tuning,EffectSourceKey source,double sourceUntil=double.PositiveInfinity) {focus.Grant(now,tuning,sourceUntil);focus.Source=source;} public void Rail(double now,AttackEnhancementTuning tuning,EffectSourceKey source,double sourceUntil=double.PositiveInfinity) {rail.Grant(now,tuning,sourceUntil);rail.Source=source;} public void CopyActiveBuffs(System.Collections.Generic.List destination,double now) {CopyWindow(focus,destination,now);CopyWindow(rail,destination,now);} private static void CopyWindow(Window window,System.Collections.Generic.List destination,double now) { if(!window.Active(now))return; var value=new PlayerBuffSnapshot(window.Source,PlayerBuffEffect.WeaponDamage,window.Tuning.DamageBonusFraction,window.Until) {FiniteUses=window.Tuning.TriggerLimit>0,RemainingTriggers=window.Remaining}; destination.Add(value); if(window.Tuning.AdditionalPierceTargets>0) {value.Effect=PlayerBuffEffect.Pierce;value.Magnitude=window.Tuning.PierceDamageFraction;value.Stacks=window.Tuning.AdditionalPierceTargets;destination.Add(value);} } public void RemoveFocus()=>focus.Clear(); public void RemoveRail()=>rail.Clear(); public GunTriggerBonus Consume(double now) { bool f=focus.Active(now),r=rail.Active(now); if(f&&focus.Tuning.TriggerLimit>0)focus.Remaining--;if(r&&rail.Tuning.TriggerLimit>0)rail.Remaining--; return new GunTriggerBonus((float)Math.Min(float.MaxValue,(double)(f?focus.Tuning.DamageBonusFraction:0)+(r?rail.Tuning.DamageBonusFraction:0)), r,Math.Max(f?focus.Tuning.PierceDamageFraction:0,r?rail.Tuning.PierceDamageFraction:0), Math.Max(f?focus.Tuning.AdditionalPierceTargets:0,r?rail.Tuning.AdditionalPierceTargets:0)); } public void Clear(){focus.Clear();rail.Clear();} private static bool Finite(double value)=>!double.IsNaN(value)&&!double.IsInfinity(value)&&value>=0; } }