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,9 @@
[gd_resource type="Resource" load_steps=2 format=3 uid="uid://dsg1vjx76ifgu"] [gd_resource type="Resource" script_class="RandomRedirectEffect" load_steps=2 format=3 uid="uid://dsg1vjx76ifgu"]
[ext_resource type="Script" uid="uid://bb6lv1djnqjaw" path="res://scripts/systems/effects/RandomRedirectEffect.cs" id="1_rfumy"] [ext_resource type="Script" uid="uid://bb6lv1djnqjaw" path="res://scripts/systems/effects/RandomRedirectEffect.cs" id="1_rfumy"]
[resource] [resource]
script = ExtResource("1_rfumy") script = ExtResource("1_rfumy")
tilesWalked = 0.2 tilesWalked = 0.2
Duration = 1.0 Duration = 3.0
Slot = "garlic" Slot = "garlic"

View file

@ -1,9 +1,18 @@
[gd_scene load_steps=5 format=3 uid="uid://b2hrv0aqbui7u"] [gd_scene load_steps=7 format=3 uid="uid://b2hrv0aqbui7u"]
[ext_resource type="Script" uid="uid://dxlwvwy3hj56x" path="res://scripts/projectiles/LinearProjectile.cs" id="1_3kc4s"] [ext_resource type="Script" uid="uid://dxlwvwy3hj56x" path="res://scripts/projectiles/LinearProjectile.cs" id="1_3kc4s"]
[ext_resource type="Texture2D" uid="uid://dq0mul65hevtt" path="res://assets/sprites/plants/pea.tres" id="2_26q5x"] [ext_resource type="Texture2D" uid="uid://dq0mul65hevtt" path="res://assets/sprites/plants/pea.tres" id="2_26q5x"]
[ext_resource type="Script" uid="uid://bb6lv1djnqjaw" path="res://scripts/systems/effects/RandomRedirectEffect.cs" id="2_fwj1o"]
[ext_resource type="PackedScene" uid="uid://1d33w4ubtury" path="res://scenes/particles/pea_particles.tscn" id="2_osqrk"] [ext_resource type="PackedScene" uid="uid://1d33w4ubtury" path="res://scenes/particles/pea_particles.tscn" id="2_osqrk"]
[sub_resource type="Resource" id="Resource_22ej5"]
script = ExtResource("2_fwj1o")
tilesWalked = 0.2
travelTime = 0.2
Duration = 0.3
Slot = "testra"
metadata/_custom_type_script = "uid://bb6lv1djnqjaw"
[sub_resource type="CircleShape2D" id="CircleShape2D_ix1sk"] [sub_resource type="CircleShape2D" id="CircleShape2D_ix1sk"]
radius = 6.0 radius = 6.0
@ -13,6 +22,7 @@ collision_mask = 8
script = ExtResource("1_3kc4s") script = ExtResource("1_3kc4s")
_speed = 3.0 _speed = 3.0
_damage = 10 _damage = 10
_impactEffect = SubResource("Resource_22ej5")
particles = ExtResource("2_osqrk") particles = ExtResource("2_osqrk")
[node name="Sprite" type="Sprite2D" parent="."] [node name="Sprite" type="Sprite2D" parent="."]

View file

@ -1,9 +1,14 @@
using Godot;
namespace Newlon; namespace Newlon;
public static class LON 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 #region Effects
[Export] private Array<Effect> _effectImmunities = new(); [Export] private Array<Effect> _effectImmunities = new();
[Export] private bool completeInvulnerability = false; [Export] private bool completeInvulnerability = false;
private readonly Dictionary<string, Effect> _activeEffectSlots = new(); private readonly Dictionary<string, EffectHandler> effectHandlers = new();
private readonly Dictionary<string, Timer> _effectSlotTimers = new();
[Signal] public delegate void EffectStartedEventHandler(Effect what); [Signal] public delegate void EffectStartedEventHandler(Effect what);
[Signal] public delegate void EffectEndedEventHandler(Effect what); [Signal] public delegate void EffectEndedEventHandler(Effect what);
@ -123,50 +122,25 @@ public partial class Entity : Node2D
public virtual void GiveEffect(Effect what) public virtual void GiveEffect(Effect what)
{ {
if (Killed) return; if (what == null ||
if (_effectImmunities.Contains(what) || completeInvulnerability) Killed ||
{ completeInvulnerability || _effectImmunities.Contains(what))
return; return;
}
string slot = what.Slot; var slot = what.Slot;
if (_activeEffectSlots.ContainsKey(slot) == false) if (effectHandlers.ContainsKey(slot) == false)
{
InitSlot(slot); InitSlot(slot);
}
if (what == _activeEffectSlots[slot]) if (effectHandlers[slot].HandledEffect == what)
{ {
EmitSignal(SignalName.EffectContinued, what); effectHandlers[slot].Restart();
} }
else 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) public void EndEffect(Effect what)
@ -174,34 +148,48 @@ public partial class Entity : Node2D
EndEffectAtSlot(what.Slot); EndEffectAtSlot(what.Slot);
} }
public Tween CreateTweenEffect(Effect effect)
{
Tween tween = CreateTween();
effectHandlers[effect.Slot].EffectTween = tween;
return tween;
}
protected void ClearEffects() protected void ClearEffects()
{ {
foreach (var slot in _activeEffectSlots.Keys) foreach (var slot in effectHandlers.Keys)
{ {
if (_activeEffectSlots[slot] != null) effectHandlers[slot].End();
{
_activeEffectSlots[slot].Exit(this);
_effectSlotTimers[slot].Stop();
_activeEffectSlots[slot] = null;
} }
} }
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() public void ProcessEffects()
{ {
if (Killed) return; foreach (var slot in effectHandlers.Keys)
foreach (string key in _activeEffectSlots.Keys) {
_activeEffectSlots[key]?.Process(this); effectHandlers[slot].Process();
}
} }
private void EndEffectAtSlot(string slot) private void EndEffectAtSlot(string slot)
{ {
_activeEffectSlots[slot].Exit(this); EmitSignal(SignalName.EffectEnded, effectHandlers[slot].HandledEffect);
_activeEffectSlots[slot] = null; effectHandlers[slot].End();
EmitSignal(SignalName.EffectEnded, _activeEffectSlots[slot]);
} }
#endregion #endregion
#region LocalTimescale #region LocalTimescale
private float _localTimescale = 1.0f; private float _localTimescale = 1.0f;

View file

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

View file

@ -1,5 +1,6 @@
using Godot; using Godot;
using Newlon.Components.Zombies; using Newlon.Components.Zombies;
using Newlon.Components;
namespace Newlon.Systems.Effects; namespace Newlon.Systems.Effects;
@ -9,14 +10,14 @@ public partial class RedirectEffect : Effect
[Export] private float tilesWalked = 0.2f; [Export] private float tilesWalked = 0.2f;
[Export] private bool down = false; [Export] private bool down = false;
public override void Enter(Node target) public override void Enter(Entity target)
{ {
if (target is RuntimeZombieData zombieData) if (target is RuntimeZombieData zombieData)
zombieData.AbleToEat = false; zombieData.AbleToEat = false;
//Animation call //Animation call
} }
public override void Exit(Node target) public override void Exit(Entity target)
{ {
if (target is RuntimeZombieData zombieData) 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 Color ColorOverride;
[Export] public float Multiplier; [Export] public float Multiplier;
public override void Enter(Node target) public override void Enter(Entity target)
{ {
if (target is Entity entity) target.LocalTimescale *= Multiplier;
{ target.Modulate = ColorOverride;
entity.LocalTimescale *= Multiplier;
entity.Modulate = ColorOverride;
}
} }
public override void Exit(Node target) public override void Exit(Entity target)
{ {
if (target is Entity entity) target.LocalTimescale /= Multiplier;
{ target.Modulate = Colors.White;
entity.LocalTimescale /= Multiplier;
entity.Modulate = Colors.White;
}
} }
public override void Process(Node target) public override void Process(Entity target)
{ {
} }