newlon/scripts/entities/Entity.cs
2025-07-23 16:07:55 +05:00

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
}