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 _effectImmunities = new(); [Export] private bool completeInvulnerability = false; private readonly Dictionary 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 }