using Godot; using Godot.Collections; using Newlon.Systems.Effects; namespace Newlon.Components; [GlobalClass] public partial class Entity : Node2D { #region Health points [Export] public float MaxHP; public float HP; [Signal] public delegate void OnHPChangedEventHandler(int deltaHP, Node origin); [Signal] public delegate void OnDamagedEventHandler(); public virtual void TakeDamage(float amount, Node origin) { HP -= amount; EmitSignal(SignalName.OnHPChanged, -amount, origin); EmitSignal(SignalName.OnDamaged); if (HP <= 0) { KillByDamage(); } } public virtual void Heal(float amount, Node origin) { EmitSignal(SignalName.OnHPChanged, amount, origin); HP += amount; } public virtual void KillByDamage() { Kill(); } public virtual void Kill() { QueueFree(); } #endregion #region Brain [Export] private AnimationPlayer _player; [Export] private AnimationTree _tree; public virtual void DisableBrain() { _player.ProcessMode = ProcessModeEnum.Always; _tree.ProcessMode = ProcessModeEnum.Always; ProcessMode = ProcessModeEnum.Disabled; } public virtual void EnableBrain() { _player.ProcessMode = ProcessModeEnum.Inherit; _tree.ProcessMode = ProcessModeEnum.Inherit; ProcessMode = ProcessModeEnum.Inherit; } #endregion #region Effects [Export] private Array _effectImmunities = new(); private readonly Dictionary _activeEffectSlots = new(); private readonly Dictionary _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)) { 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; } #endregion }