208 lines
4.6 KiB
C#
208 lines
4.6 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();
|
|
|
|
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 virtual void KillByDamage()
|
|
{
|
|
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, Effect> _activeEffectSlots = new();
|
|
private readonly Dictionary<string, Timer> _effectSlotTimers = 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 (_effectImmunities.Contains(what) || completeInvulnerability)
|
|
{
|
|
return;
|
|
}
|
|
|
|
string slot = what.Slot;
|
|
if (_activeEffectSlots.ContainsKey(slot) == false)
|
|
{
|
|
InitSlot(slot);
|
|
}
|
|
|
|
if (what == _activeEffectSlots[slot])
|
|
{
|
|
EmitSignal(SignalName.EffectContinued, what);
|
|
}
|
|
else
|
|
{
|
|
EmitSignal(SignalName.EffectStarted, what);
|
|
}
|
|
|
|
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)
|
|
{
|
|
EndEffectAtSlot(what.Slot);
|
|
}
|
|
|
|
public void ProcessEffects()
|
|
{
|
|
foreach (string key in _activeEffectSlots.Keys)
|
|
_activeEffectSlots[key]?.Process(this);
|
|
}
|
|
|
|
private void EndEffectAtSlot(string slot)
|
|
{
|
|
_activeEffectSlots[slot].Exit(this);
|
|
_activeEffectSlots[slot] = null;
|
|
|
|
EmitSignal(SignalName.EffectEnded, _activeEffectSlots[slot]);
|
|
|
|
}
|
|
#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
|
|
}
|