218 lines
4.8 KiB
C#
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
|
|
}
|