new effect system

This commit is contained in:
Rendo 2025-07-30 00:56:53 +05:00
commit 22b02c4590
11 changed files with 146 additions and 89 deletions

View file

@ -1,9 +1,14 @@
using Godot;
namespace Newlon;
public static class LON
{
public static float Pr(string path)
public static void ForceFinishTween(Tween tween)
{
return 0;
if (tween == null) return;
tween.Pause();
tween.CustomStep(Mathf.Inf);
}
}

View file

@ -113,8 +113,7 @@ public partial class Entity : Node2D
#region Effects
[Export] private Array<Effect> _effectImmunities = new();
[Export] private bool completeInvulnerability = false;
private readonly Dictionary<string, Effect> _activeEffectSlots = new();
private readonly Dictionary<string, Timer> _effectSlotTimers = new();
private readonly Dictionary<string, EffectHandler> effectHandlers = new();
[Signal] public delegate void EffectStartedEventHandler(Effect what);
[Signal] public delegate void EffectEndedEventHandler(Effect what);
@ -123,50 +122,25 @@ public partial class Entity : Node2D
public virtual void GiveEffect(Effect what)
{
if (Killed) return;
if (_effectImmunities.Contains(what) || completeInvulnerability)
{
if (what == null ||
Killed ||
completeInvulnerability || _effectImmunities.Contains(what))
return;
}
string slot = what.Slot;
if (_activeEffectSlots.ContainsKey(slot) == false)
{
var slot = what.Slot;
if (effectHandlers.ContainsKey(slot) == false)
InitSlot(slot);
}
if (what == _activeEffectSlots[slot])
if (effectHandlers[slot].HandledEffect == what)
{
EmitSignal(SignalName.EffectContinued, what);
effectHandlers[slot].Restart();
}
else
{
EmitSignal(SignalName.EffectStarted, what);
effectHandlers[slot].HandledEffect = what;
effectHandlers[slot].Start();
}
if (_activeEffectSlots[slot] != null)
{
_effectSlotTimers[slot].Stop();
_activeEffectSlots[slot].Exit(this);
}
_effectSlotTimers[slot].WaitTime = what.Duration;
_effectSlotTimers[slot].Start();
what.Enter(this);
_activeEffectSlots[slot] = what;
}
private void InitSlot(string key)
{
_activeEffectSlots.Add(key, null);
var timer = new Timer() { Autostart = false, OneShot = true };
AddChild(timer);
timer.Timeout += () => { EndEffectAtSlot(key); };
_effectSlotTimers.Add(key, timer);
}
public void EndEffect(Effect what)
@ -174,34 +148,48 @@ public partial class Entity : Node2D
EndEffectAtSlot(what.Slot);
}
public Tween CreateTweenEffect(Effect effect)
{
Tween tween = CreateTween();
effectHandlers[effect.Slot].EffectTween = tween;
return tween;
}
protected void ClearEffects()
{
foreach (var slot in _activeEffectSlots.Keys)
foreach (var slot in effectHandlers.Keys)
{
if (_activeEffectSlots[slot] != null)
{
_activeEffectSlots[slot].Exit(this);
_effectSlotTimers[slot].Stop();
_activeEffectSlots[slot] = null;
}
effectHandlers[slot].End();
}
}
private void InitSlot(string key)
{
effectHandlers.Add(key, new EffectHandler());
effectHandlers[key].handler = this;
effectHandlers[key].EffectTimer = new();
AddChild(effectHandlers[key].EffectTimer);
effectHandlers[key].EffectTimer.Name = key + "Timer";
effectHandlers[key].EffectTimer.Timeout += () => { EndEffectAtSlot(key); };
}
public void ProcessEffects()
{
if (Killed) return;
foreach (string key in _activeEffectSlots.Keys)
_activeEffectSlots[key]?.Process(this);
foreach (var slot in effectHandlers.Keys)
{
effectHandlers[slot].Process();
}
}
private void EndEffectAtSlot(string slot)
{
_activeEffectSlots[slot].Exit(this);
_activeEffectSlots[slot] = null;
EmitSignal(SignalName.EffectEnded, _activeEffectSlots[slot]);
EmitSignal(SignalName.EffectEnded, effectHandlers[slot].HandledEffect);
effectHandlers[slot].End();
}
#endregion
#region LocalTimescale
private float _localTimescale = 1.0f;

View file

@ -1,4 +1,5 @@
using Godot;
using Newlon.Components;
namespace Newlon.Systems.Effects;
@ -7,7 +8,12 @@ public abstract partial class Effect : Resource
{
[Export] public float Duration;
[Export] public string Slot;
public abstract void Enter(Node target);
public abstract void Process(Node target);
public abstract void Exit(Node target);
public abstract void Enter(Entity target);
public abstract void Process(Entity target);
public abstract void Exit(Entity target);
private Tween CreateTween(Entity fromEntity)
{
return fromEntity.CreateTweenEffect(this);
}
}

View file

@ -0,0 +1,48 @@
using Godot;
using Newlon.Components;
namespace Newlon.Systems.Effects;
public partial class EffectHandler : RefCounted
{
public Entity handler;
public Effect HandledEffect;
public Timer EffectTimer;
public Tween EffectTween;
public void Start()
{
EffectTimer.WaitTime = HandledEffect.Duration;
EffectTimer.Start();
HandledEffect.Enter(handler);
}
public void End()
{
HandledEffect.Exit(handler);
HandledEffect = null;
EffectTimer.Stop();
if (EffectTween != null)
{
LON.ForceFinishTween(EffectTween);
EffectTween.Kill();
}
}
public void Process()
{
HandledEffect.Process(handler);
}
public void Restart()
{
HandledEffect.Exit(handler);
EffectTimer.Stop();
if (EffectTween != null)
{
LON.ForceFinishTween(EffectTween);
EffectTween.Kill();
}
HandledEffect.Enter(handler);
EffectTimer.Start();
}
}

View file

@ -0,0 +1 @@
uid://csxb8rllqjb1t

View file

@ -8,19 +8,16 @@ public partial class PermanentSpeedEffect : Effect
{
[Export] public float Multiplier;
public override void Enter(Node target)
public override void Enter(Entity target)
{
if (target is Entity entity)
{
entity.LocalTimescale *= Multiplier;
}
target.LocalTimescale *= Multiplier;
}
public override void Exit(Node target)
public override void Exit(Entity target)
{
}
public override void Process(Node target)
public override void Process(Entity target)
{
}

View file

@ -1,4 +1,5 @@
using Godot;
using Newlon.Components;
using Newlon.Components.Zombies;
namespace Newlon.Systems.Effects;
@ -7,9 +8,10 @@ namespace Newlon.Systems.Effects;
public partial class RandomRedirectEffect : Effect
{
[Export] private float tilesWalked = 0.2f;
[Export] private float travelTime = 1;
RandomNumberGenerator RandomNumberGenerator;
public override void Enter(Node target)
public override void Enter(Entity target)
{
if (RandomNumberGenerator == null)
{
@ -19,9 +21,14 @@ public partial class RandomRedirectEffect : Effect
if (target is RuntimeZombieData zombieData)
zombieData.AbleToEat = false;
//Animation call
Animation(target);
}
public override void Exit(Node target)
public override void Exit(Entity target)
{
}
private void Animation(Entity target)
{
if (target is RuntimeZombieData zombieData)
{
@ -46,14 +53,14 @@ public partial class RandomRedirectEffect : Effect
}
}
zombieData.AbleToEat = false;
var tween = zombieData.CreateTween();
tween.TweenProperty(zombieData, "position:y", zombieData.GlobalPosition.Y + FieldParams.TileHeight * mult, Duration);
tween.Parallel().TweenProperty(zombieData, "position:x", zombieData.GlobalPosition.X - FieldParams.TileHeight * tilesWalked, Duration);
var tween = zombieData.CreateTweenEffect(this);
tween.TweenInterval(Duration-travelTime);
tween.TweenProperty(zombieData, "position:y", zombieData.GlobalPosition.Y + FieldParams.TileHeight * mult, travelTime);
tween.Parallel().TweenProperty(zombieData, "position:x", zombieData.GlobalPosition.X - FieldParams.TileHeight * tilesWalked, travelTime);
tween.TweenCallback(Callable.From(() => { zombieData.AbleToEat = true; }));
}
}
public override void Process(Node target)
public override void Process(Entity target)
{
}

View file

@ -1,5 +1,6 @@
using Godot;
using Newlon.Components.Zombies;
using Newlon.Components;
namespace Newlon.Systems.Effects;
@ -9,14 +10,14 @@ public partial class RedirectEffect : Effect
[Export] private float tilesWalked = 0.2f;
[Export] private bool down = false;
public override void Enter(Node target)
public override void Enter(Entity target)
{
if (target is RuntimeZombieData zombieData)
zombieData.AbleToEat = false;
//Animation call
}
public override void Exit(Node target)
public override void Exit(Entity target)
{
if (target is RuntimeZombieData zombieData)
{
@ -48,7 +49,7 @@ public partial class RedirectEffect : Effect
}
}
public override void Process(Node target)
public override void Process(Entity target)
{
}

View file

@ -9,25 +9,19 @@ public partial class SlownessEffect : Effect
[Export] public Color ColorOverride;
[Export] public float Multiplier;
public override void Enter(Node target)
public override void Enter(Entity target)
{
if (target is Entity entity)
{
entity.LocalTimescale *= Multiplier;
entity.Modulate = ColorOverride;
}
target.LocalTimescale *= Multiplier;
target.Modulate = ColorOverride;
}
public override void Exit(Node target)
public override void Exit(Entity target)
{
if (target is Entity entity)
{
entity.LocalTimescale /= Multiplier;
entity.Modulate = Colors.White;
}
target.LocalTimescale /= Multiplier;
target.Modulate = Colors.White;
}
public override void Process(Node target)
public override void Process(Entity target)
{
}