newlon/scripts/entities/Entity.cs
2025-07-30 00:56:53 +05:00

218 lines
4.8 KiB
C#

using Godot;
using Godot.Collections;
using Newlon.Components.Level;
using Newlon.Systems.Effects;
namespace Newlon.Components;
[GlobalClass]
public partial class Entity : Node2D
{
#region Health points
[Export] public float MaxHP;
[Export]public float HP;
[Signal] public delegate void OnHPChangedEventHandler(EntitySignalContext context);
[Signal] public delegate void OnDamagedEventHandler();
[Signal] public delegate void HasBeenKilledEventHandler(Entity who);
public virtual void TakeDamage(float amount, Node origin)
{
if (amount > 0)
EmitSignal(SignalName.OnDamaged);
var context = new EntitySignalContext()
{
target = this,
source = (Entity)origin,
actionAmount = amount
};
if (HP - amount <= 0)
{
context.deltaHP = -HP;
EmitSignal(SignalName.OnHPChanged, context);
HP = 0;
KillByDamage();
}
else
{
context.deltaHP = -amount;
HP -= amount;
EmitSignal(SignalName.OnHPChanged, context);
}
}
public virtual void Heal(float amount, Node origin)
{
var context = new EntitySignalContext()
{
target = this,
source = (Entity)origin,
actionAmount = amount
};
if (HP + amount > MaxHP)
{
context.deltaHP = MaxHP - HP;
EmitSignal(SignalName.OnHPChanged, context);
HP = MaxHP;
}
else
{
context.deltaHP = amount;
HP += amount;
EmitSignal(SignalName.OnHPChanged, context);
}
}
public bool Killed = false;
public virtual void KillByDamage()
{
if (Killed) return;
Killed = true;
EmitSignal(SignalName.HasBeenKilled,this);
ClearEffects();
Kill();
}
public virtual void Kill()
{
QueueFree();
}
#endregion
#region Brain
[Export] private AnimationPlayer _player;
[Export] private AnimationTree _tree;
private bool forceToggledBrain = false;
private bool brainEnabled = true;
public virtual void DisableBrain(bool force = true)
{
if (brainEnabled == false) return;
if (_player != null)
_player.ProcessMode = ProcessModeEnum.Pausable;
if (_tree != null)
_tree.ProcessMode = ProcessModeEnum.Pausable;
ProcessMode = ProcessModeEnum.Disabled;
forceToggledBrain = force;
brainEnabled = false;
}
public virtual void EnableBrain(bool force = true)
{
if (brainEnabled) return;
if (_player != null)
_player.ProcessMode = ProcessModeEnum.Inherit;
if (_tree != null)
_tree.ProcessMode = ProcessModeEnum.Inherit;
ProcessMode = ProcessModeEnum.Inherit;
forceToggledBrain = force;
brainEnabled = true;
}
#endregion
#region Effects
[Export] private Array<Effect> _effectImmunities = new();
[Export] private bool completeInvulnerability = false;
private readonly Dictionary<string, EffectHandler> effectHandlers = new();
[Signal] public delegate void EffectStartedEventHandler(Effect what);
[Signal] public delegate void EffectEndedEventHandler(Effect what);
[Signal] public delegate void EffectContinuedEventHandler(Effect what);
public virtual void GiveEffect(Effect what)
{
if (what == null ||
Killed ||
completeInvulnerability || _effectImmunities.Contains(what))
return;
var slot = what.Slot;
if (effectHandlers.ContainsKey(slot) == false)
InitSlot(slot);
if (effectHandlers[slot].HandledEffect == what)
{
effectHandlers[slot].Restart();
}
else
{
effectHandlers[slot].HandledEffect = what;
effectHandlers[slot].Start();
}
}
public void EndEffect(Effect what)
{
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 effectHandlers.Keys)
{
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()
{
foreach (var slot in effectHandlers.Keys)
{
effectHandlers[slot].Process();
}
}
private void EndEffectAtSlot(string slot)
{
EmitSignal(SignalName.EffectEnded, effectHandlers[slot].HandledEffect);
effectHandlers[slot].End();
}
#endregion
#region LocalTimescale
private float _localTimescale = 1.0f;
[Signal] public delegate void OnLocalTimescaleChangedEventHandler(float scale);
public float LocalTimescale
{
get => _localTimescale;
set
{
_localTimescale = value;
EmitSignal(SignalName.OnLocalTimescaleChanged, _localTimescale);
}
}
#endregion
#region Godot overrides
public override void _Ready()
{
HP = MaxHP;
if (RuntimeLevelData.Instance != null)
{
if (RuntimeLevelData.Instance.GetLevelState() != RuntimeLevelData.LevelStates.Game) DisableBrain(false);
}
}
#endregion
}