Merge branch 'entity-rework'

Entity rework unifies code, allowing plants to use:
1. Zombie nodes
2. Effects system
3. Local timescale

Also it allows animation rework for zombies (and plants)..
This commit is contained in:
Rendo 2025-07-07 17:54:05 +05:00
commit 5fe7cb7d68
41 changed files with 286 additions and 277 deletions

View file

@ -13,7 +13,7 @@ dest_files=["res://.godot/imported/yuck.mp3-300336a3ee536c8e6db69a73706c7ea5.mp3
[params] [params]
loop=false loop=false
loop_offset=0 loop_offset=0.0
bpm=0 bpm=0.0
beat_count=0 beat_count=0
bar_beats=4 bar_beats=4

View file

@ -22,7 +22,7 @@ config/windows_native_icon="res://icon.ico"
LevelController="*res://scripts/LevelController.cs" LevelController="*res://scripts/LevelController.cs"
Cursor="*res://scripts/Cursor.cs" Cursor="*res://scripts/Cursor.cs"
GameRegistry="*res://scripts/systems/GameRegistry.cs" GameRegistry="*res://scripts/systems/GameRegistry.cs"
Cheats="res://scripts/debug/Cheats.cs" Cheats="*res://scripts/debug/Cheats.cs"
AudioSequencer="*res://scenes/audio_sequencer.tscn" AudioSequencer="*res://scenes/audio_sequencer.tscn"
[display] [display]

View file

@ -5,4 +5,4 @@
[resource] [resource]
script = ExtResource("1_rfumy") script = ExtResource("1_rfumy")
Duration = 0.25 Duration = 0.25
Slot = 3 Slot = "garlic"

View file

@ -1,10 +1,10 @@
[gd_resource type="Resource" load_steps=2 format=3 uid="uid://7uj0oe656jfx"] [gd_resource type="Resource" load_steps=2 format=3 uid="uid://7uj0oe656jfx"]
[ext_resource type="Script" path="res://scripts/systems/effects/SlownessEffect.cs" id="1_8md01"] [ext_resource type="Script" uid="uid://dyc7fc5bfkdii" path="res://scripts/systems/effects/SlownessEffect.cs" id="1_8md01"]
[resource] [resource]
script = ExtResource("1_8md01") script = ExtResource("1_8md01")
ColorOverride = Color(0, 1, 1, 1) ColorOverride = Color(0, 1, 1, 1)
Multiplier = 0.75 Multiplier = 0.75
Duration = 3.25 Duration = 3.25
Slot = 0 Slot = "freeze_slow"

View file

@ -1,8 +1,10 @@
[gd_scene load_steps=44 format=3 uid="uid://bgqmwsb6ynm81"] [gd_scene load_steps=46 format=3 uid="uid://bgqmwsb6ynm81"]
[ext_resource type="PackedScene" uid="uid://co11v3w8hbwgf" path="res://scenes/entities/Zombies/zombie.tscn" id="1_fnu7s"] [ext_resource type="PackedScene" uid="uid://co11v3w8hbwgf" path="res://scenes/entities/Zombies/zombie.tscn" id="1_fnu7s"]
[ext_resource type="Shader" uid="uid://d0eo5uuj222c4" path="res://assets/shaders/CG_color_blender.gdshader" id="2_6qr4h"] [ext_resource type="Shader" uid="uid://d0eo5uuj222c4" path="res://assets/shaders/CG_color_blender.gdshader" id="2_6qr4h"]
[ext_resource type="Script" uid="uid://bcc7skl7ts6sh" path="res://scripts/systems/effects/Effect.cs" id="2_txjqc"]
[ext_resource type="Texture2D" uid="uid://cse1504ao4g8e" path="res://assets/sprites/atlases/zombies/hobo.png" id="3_6qr4h"] [ext_resource type="Texture2D" uid="uid://cse1504ao4g8e" path="res://assets/sprites/atlases/zombies/hobo.png" id="3_6qr4h"]
[ext_resource type="Resource" uid="uid://dsg1vjx76ifgu" path="res://resources/effects/GarlicEffect.tres" id="3_b583s"]
[ext_resource type="Script" uid="uid://dt5uj25u0g6y3" path="res://scripts/components/particles/FallParticle.cs" id="3_tu6af"] [ext_resource type="Script" uid="uid://dt5uj25u0g6y3" path="res://scripts/components/particles/FallParticle.cs" id="3_tu6af"]
[ext_resource type="AnimationLibrary" uid="uid://cuh2kyb21hmkm" path="res://resources/animations/zombies/basic_zombie.res" id="4_02fim"] [ext_resource type="AnimationLibrary" uid="uid://cuh2kyb21hmkm" path="res://resources/animations/zombies/basic_zombie.res" id="4_02fim"]
[ext_resource type="Script" uid="uid://c5v2og85t7s6j" path="res://scripts/components/zombies/behaviours/HoboBehaviour.cs" id="4_5selg"] [ext_resource type="Script" uid="uid://c5v2og85t7s6j" path="res://scripts/components/zombies/behaviours/HoboBehaviour.cs" id="4_5selg"]
@ -227,8 +229,9 @@ percentage_value = 0.0
mult_value = 1.0 mult_value = 1.0
[node name="Hobo" node_paths=PackedStringArray("_armor") instance=ExtResource("1_fnu7s")] [node name="Hobo" node_paths=PackedStringArray("_armor") instance=ExtResource("1_fnu7s")]
_maxHP = 130
_armor = NodePath("CanArmor") _armor = NodePath("CanArmor")
MaxHP = 130.0
_effectImmunities = Array[ExtResource("2_txjqc")]([ExtResource("3_b583s")])
[node name="CanvasGroup" parent="." index="0"] [node name="CanvasGroup" parent="." index="0"]
material = SubResource("ShaderMaterial_w8pya") material = SubResource("ShaderMaterial_w8pya")
@ -282,7 +285,6 @@ _speed = SubResource("Resource_4248q")
[node name="CanArmor" type="Node" parent="." index="7"] [node name="CanArmor" type="Node" parent="." index="7"]
script = ExtResource("4_w8pya") script = ExtResource("4_w8pya")
MaxHP = 385
metadata/_custom_type_script = "uid://fd4im1fmwc5n" metadata/_custom_type_script = "uid://fd4im1fmwc5n"
[node name="Behaviour" type="Node" parent="." index="8" node_paths=PackedStringArray("_eatBox", "_animationTree")] [node name="Behaviour" type="Node" parent="." index="8" node_paths=PackedStringArray("_eatBox", "_animationTree")]

View file

@ -1,4 +1,4 @@
[gd_scene load_steps=24 format=3 uid="uid://co11v3w8hbwgf"] [gd_scene load_steps=27 format=3 uid="uid://co11v3w8hbwgf"]
[ext_resource type="Script" uid="uid://dildme6epx8l4" path="res://scripts/components/zombies/RuntimeZombieData.cs" id="1_qq3f1"] [ext_resource type="Script" uid="uid://dildme6epx8l4" path="res://scripts/components/zombies/RuntimeZombieData.cs" id="1_qq3f1"]
[ext_resource type="Texture2D" uid="uid://bwdqbrnn7ygtr" path="res://assets/sprites/atlases/zombies/Зондби.png" id="2_4pdxh"] [ext_resource type="Texture2D" uid="uid://bwdqbrnn7ygtr" path="res://assets/sprites/atlases/zombies/Зондби.png" id="2_4pdxh"]
@ -16,6 +16,9 @@
[ext_resource type="AudioStream" uid="uid://bg76miyscfvhu" path="res://assets/audio/sfx/groan.tres" id="12_ad42i"] [ext_resource type="AudioStream" uid="uid://bg76miyscfvhu" path="res://assets/audio/sfx/groan.tres" id="12_ad42i"]
[ext_resource type="Script" uid="uid://cnn0ymuhypdff" path="res://scripts/audio/ChannelPlaylist.cs" id="12_he8da"] [ext_resource type="Script" uid="uid://cnn0ymuhypdff" path="res://scripts/audio/ChannelPlaylist.cs" id="12_he8da"]
[ext_resource type="AudioStream" uid="uid://w0qfwds4o3ti" path="res://assets/audio/sfx/hit_generic.tres" id="12_vjrlo"] [ext_resource type="AudioStream" uid="uid://w0qfwds4o3ti" path="res://assets/audio/sfx/hit_generic.tres" id="12_vjrlo"]
[ext_resource type="Script" uid="uid://b8r6fxsfjdo3a" path="res://scripts/audio/EffectBasedPlayer.cs" id="17_ltj46"]
[ext_resource type="Resource" uid="uid://dsg1vjx76ifgu" path="res://resources/effects/GarlicEffect.tres" id="18_2q05d"]
[ext_resource type="Resource" uid="uid://7uj0oe656jfx" path="res://resources/effects/SnowSlow.tres" id="19_ccrjo"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_63ls2"] [sub_resource type="ShaderMaterial" id="ShaderMaterial_63ls2"]
resource_local_to_scene = true resource_local_to_scene = true
@ -66,9 +69,9 @@ mult_value = 1.0
[node name="Zombie" type="Node2D"] [node name="Zombie" type="Node2D"]
y_sort_enabled = true y_sort_enabled = true
script = ExtResource("1_qq3f1") script = ExtResource("1_qq3f1")
_maxHP = 70
garlicSound = ExtResource("2_hh4qh") garlicSound = ExtResource("2_hh4qh")
freezeSound = ExtResource("3_ltj46") freezeSound = ExtResource("3_ltj46")
MaxHP = 70.0
[node name="CanvasGroup" type="CanvasGroup" parent="."] [node name="CanvasGroup" type="CanvasGroup" parent="."]
material = SubResource("ShaderMaterial_63ls2") material = SubResource("ShaderMaterial_63ls2")
@ -137,6 +140,11 @@ wait_time = 5.0
one_shot = true one_shot = true
autostart = true autostart = true
[node name="EffectPlayer" type="Node" parent="."]
script = ExtResource("17_ltj46")
effectsToMap = Array[Resource]([ExtResource("18_2q05d"), ExtResource("19_ccrjo")])
streamsToMapTo = Array[AudioStream]([ExtResource("2_hh4qh"), ExtResource("3_ltj46")])
[connection signal="OnDamaged" from="." to="HitPlayer" method="Play"] [connection signal="OnDamaged" from="." to="HitPlayer" method="Play"]
[connection signal="OnHPChanged" from="." to="CanvasGroup" method="DamageFlash"] [connection signal="OnHPChanged" from="." to="CanvasGroup" method="DamageFlash"]
[connection signal="area_entered" from="Eatbox" to="Eatbox" method="OnAreaEntered"] [connection signal="area_entered" from="Eatbox" to="Eatbox" method="OnAreaEntered"]

View file

@ -84,6 +84,8 @@ graph_offset = Vector2(-78.082, -71.7578)
size = Vector2(22, 32) size = Vector2(22, 32)
[node name="Aloe" instance=ExtResource("1_n25yi")] [node name="Aloe" instance=ExtResource("1_n25yi")]
MaxHP = 30.0
_effectImmunities = Array[Resource]([])
[node name="Sprite2D" parent="." index="0"] [node name="Sprite2D" parent="." index="0"]
position = Vector2(9, -14) position = Vector2(9, -14)

View file

@ -50,7 +50,8 @@ blend_mode = 1
size = Vector2(32, 29) size = Vector2(32, 29)
[node name="Garlic" instance=ExtResource("1_5i0e6")] [node name="Garlic" instance=ExtResource("1_5i0e6")]
_maxHP = 200 MaxHP = 200.0
_effectImmunities = Array[Resource]([])
[node name="Sprite2D" parent="." index="0"] [node name="Sprite2D" parent="." index="0"]
texture = ExtResource("2_w2jbi") texture = ExtResource("2_w2jbi")

View file

@ -62,6 +62,7 @@ resource_local_to_scene = true
size = Vector2(20, 44) size = Vector2(20, 44)
[node name="Peashooter" instance=ExtResource("1_pyk3o")] [node name="Peashooter" instance=ExtResource("1_pyk3o")]
MaxHP = 30.0
[node name="Sprite2D" parent="." index="0"] [node name="Sprite2D" parent="." index="0"]
texture = ExtResource("2_14qlx") texture = ExtResource("2_14qlx")

View file

@ -68,7 +68,8 @@ size = Vector2(15, 27)
size = Vector2(34, 19) size = Vector2(34, 19)
[node name="Potato mine" instance=ExtResource("1_dj7ul")] [node name="Potato mine" instance=ExtResource("1_dj7ul")]
_maxHP = 20 MaxHP = 20.0
_effectImmunities = Array[Resource]([])
[node name="Sprite2D" parent="." index="0"] [node name="Sprite2D" parent="." index="0"]
texture = ExtResource("2_sneas") texture = ExtResource("2_sneas")

View file

@ -26,6 +26,8 @@ blend_mode = 1
size = Vector2(49, 38) size = Vector2(49, 38)
[node name="Spikeweed" instance=ExtResource("1_vmbvr")] [node name="Spikeweed" instance=ExtResource("1_vmbvr")]
MaxHP = 30.0
_effectImmunities = Array[Resource]([])
[node name="Sprite2D" parent="." index="0"] [node name="Sprite2D" parent="." index="0"]
texture = ExtResource("2_ffrjr") texture = ExtResource("2_ffrjr")

View file

@ -55,13 +55,14 @@ transitions = ["Start", "sunflower_idle", SubResource("AnimationNodeStateMachine
size = Vector2(26, 48) size = Vector2(26, 48)
[node name="Sunflower" instance=ExtResource("1_bikjn")] [node name="Sunflower" instance=ExtResource("1_bikjn")]
_maxHP = 30 MaxHP = 30.0
_effectImmunities = Array[Resource]([])
[node name="Sprite2D" parent="." index="0"] [node name="Sprite2D" parent="." index="0"]
texture = ExtResource("2_fwcda") texture = ExtResource("2_fwcda")
hframes = 9 hframes = 9
vframes = 2 vframes = 2
frame = 1 frame = 4
[node name="AnimationPlayer" parent="." index="1"] [node name="AnimationPlayer" parent="." index="1"]
libraries = { libraries = {

View file

@ -28,12 +28,14 @@ blend_mode = 1
size = Vector2(33, 46) size = Vector2(33, 46)
[node name="Wallnut" instance=ExtResource("1_fluxn")] [node name="Wallnut" instance=ExtResource("1_fluxn")]
_maxHP = 600 MaxHP = 600.0
_effectImmunities = Array[Resource]([])
[node name="Sprite2D" parent="." index="0"] [node name="Sprite2D" parent="." index="0"]
texture = ExtResource("2_o5tda") texture = ExtResource("2_o5tda")
hframes = 12 hframes = 12
vframes = 3 vframes = 3
frame = 3
[node name="AnimationPlayer" parent="." index="1"] [node name="AnimationPlayer" parent="." index="1"]
libraries = { libraries = {

View file

@ -18,7 +18,6 @@ script = ExtResource("1_fkydi")
_speed = 3.0 _speed = 3.0
_damage = 10 _damage = 10
_impactEffect = ExtResource("2_fn62x") _impactEffect = ExtResource("2_fn62x")
_damageType = 1
particles = ExtResource("3_t6hp0") particles = ExtResource("3_t6hp0")
[node name="Sprite" type="Sprite2D" parent="."] [node name="Sprite" type="Sprite2D" parent="."]

View file

@ -0,0 +1,27 @@
using Godot;
using Godot.Collections;
using System.Collections.Generic;
using Newlon.Components;
using Newlon.Systems.Effects;
public partial class EffectBasedPlayer : Node
{
[Export] public Array<Effect> effectsToMap;
[Export] public Array<AudioStream> streamsToMapTo;
private System.Collections.Generic.Dictionary<Effect, AudioStream> effectToAudioMap = new();
public override void _Ready()
{
GetParent<Entity>().EffectStarted += OnEffectStarted;
for (int i = 0; i < effectsToMap.Count; i++)
{
effectToAudioMap.Add(effectsToMap[i], streamsToMapTo[i]);
}
}
public void OnEffectStarted(Effect what)
{
if (effectToAudioMap.ContainsKey(what) == false) return;
AudioSequencer.Play(what.Slot, effectToAudioMap[what]);
}
}

View file

@ -0,0 +1 @@
uid://b8r6fxsfjdo3a

View file

@ -6,13 +6,13 @@ namespace Newlon.Components;
public partial class Armor : Node public partial class Armor : Node
{ {
[Signal] [Signal]
public delegate void ArmorDamagedEventHandler(int hp); public delegate void ArmorDamagedEventHandler(float hp);
[Signal] [Signal]
public delegate void ArmorLostEventHandler(); public delegate void ArmorLostEventHandler();
[Export] [Export]
public int MaxHP { get; private set; } public float MaxHP { get; private set; }
private int _hp; private float _hp;
private bool _lost = false; private bool _lost = false;
public override void _Ready() public override void _Ready()
@ -20,11 +20,11 @@ public partial class Armor : Node
_hp = MaxHP; _hp = MaxHP;
} }
public int RecieveDamage(int damage) public float RecieveDamage(float damage)
{ {
if(_lost) if(_lost)
return damage; return damage;
int returnAmount = 0; float returnAmount = 0;
_hp -= damage; _hp -= damage;
if(_hp <= 0) if(_hp <= 0)
{ {
@ -37,11 +37,11 @@ public partial class Armor : Node
return returnAmount; return returnAmount;
} }
public int Heal(int amount) public float Heal(float amount)
{ {
if(_lost) if(_lost)
return amount; return amount;
int returnAmount = 0; float returnAmount = 0;
_hp += amount; _hp += amount;
if (_hp >= MaxHP) if (_hp >= MaxHP)
{ {

View file

@ -14,9 +14,9 @@ public partial class DegradingSprite : Sprite2D
armor.ArmorDamaged += OnZombieHPChanged; armor.ArmorDamaged += OnZombieHPChanged;
} }
private void OnZombieHPChanged(int hp) private void OnZombieHPChanged(float hp)
{ {
float percent = (float)hp / (float)armor.MaxHP; float percent = hp / armor.MaxHP;
for (int i = 0; i < degradationStages.Count; i++) for (int i = 0; i < degradationStages.Count; i++)
{ {
if (percent <= thresholdPercentage[i]) if (percent <= thresholdPercentage[i])

View file

@ -0,0 +1,153 @@
using Godot;
using Godot.Collections;
using Newlon.Systems.Effects;
namespace Newlon.Components;
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
public virtual void DisableBrain()
{
GetNode<AnimationPlayer>("AnimationPlayer").ProcessMode = ProcessModeEnum.Pausable;
ProcessMode = ProcessModeEnum.Disabled;
}
public virtual void EnableBrain()
{
GetNode<AnimationPlayer>("AnimationPlayer").ProcessMode = ProcessModeEnum.Inherit;
ProcessMode = ProcessModeEnum.Inherit;
}
#endregion
#region Effects
[Export] private Array<Effect> _effectImmunities = new();
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))
{
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
}

View file

@ -0,0 +1 @@
uid://3tw88wj1nrj1

View file

@ -1,17 +0,0 @@
using Godot;
namespace Newlon.Components;
//
// Base interface for entities
//
public interface IEntity
{
public int Hp { get; }
public int MaxHp { get; }
public void TakeDamage(int amount, Node origin, Utility.DamageTypes damageType = Utility.DamageTypes.PHYSICAL);
public void Heal(int amount, Node origin);
public void DisableBrain();
public void EnableBrain();
}

View file

@ -1 +0,0 @@
uid://cjdeq452vk2ll

View file

@ -1,6 +0,0 @@
namespace Newlon.Components;
public interface ILocalTimescale
{
public float LocalTimescale { get; set; }
}

View file

@ -1 +0,0 @@
uid://bxu6ljkmmlgd5

View file

@ -17,8 +17,6 @@ public partial class LinearProjectile : Area2D, IProjectile
[Export] [Export]
private Effect _impactEffect; private Effect _impactEffect;
[Export] [Export]
private Utility.DamageTypes _damageType = Utility.DamageTypes.PHYSICAL;
[Export]
private PackedScene particles; private PackedScene particles;
private int _line; private int _line;
private bool used = false; private bool used = false;
@ -33,13 +31,13 @@ public partial class LinearProjectile : Area2D, IProjectile
public void OnAreaEntered(Area2D area) public void OnAreaEntered(Area2D area)
{ {
if (used == true) return; if (used == true) return;
var entity = area.GetParent<IEntity>(); var entity = area.GetParent<Entity>();
if (entity != null) if (entity != null)
{ {
entity.TakeDamage(_damage,this,_damageType); entity.TakeDamage(_damage,this);
used = true; used = true;
if (entity is IEffectHandler effectHandler && _impactEffect != null) if (_impactEffect != null)
effectHandler.GiveEffect(_impactEffect); entity.GiveEffect(_impactEffect);
PoolContainer.Instance.SpawnParticles(particles, GlobalPosition); PoolContainer.Instance.SpawnParticles(particles, GlobalPosition);

View file

@ -44,7 +44,7 @@ public partial class Previewport : SubViewport
title.Text = Tr(resource.name_key); title.Text = Tr(resource.name_key);
description.Text = Tr(resource.description_key); description.Text = Tr(resource.description_key);
AddChild(current_display); AddChild(current_display);
if (current_display is IEntity entity) if (current_display is Entity entity)
entity.DisableBrain(); entity.DisableBrain();
} }

View file

@ -23,12 +23,12 @@ public partial class PoolContainer : Node2D
public static PoolContainer Instance { get; private set; } public static PoolContainer Instance { get; private set; }
public Dictionary<Vector2, IEntity>[] EntityField = { new(), new(), new() }; public Dictionary<Vector2, Entity>[] EntityField = { new(), new(), new() };
public override void _Ready() public override void _Ready()
{ {
Instance = this; Instance = this;
} }
public bool TryGetEntity(Vector2 key, out IEntity result, int layer = 1) public bool TryGetEntity(Vector2 key, out Entity result, int layer = 1)
{ {
if (EntityField[layer].ContainsKey(key)) if (EntityField[layer].ContainsKey(key))
{ {

View file

@ -7,7 +7,7 @@ public partial class Eyesight : Area2D
{ {
private bool _enemyDetected; private bool _enemyDetected;
public bool EnemyDetected => _enemyDetected; public bool EnemyDetected => _enemyDetected;
private readonly List<IEntity> _detectedEntities = new List<IEntity>(); private readonly List<Entity> _detectedEntities = new List<Entity>();
private RuntimePlantData _plantData; private RuntimePlantData _plantData;
public override void _Ready() public override void _Ready()
@ -19,7 +19,7 @@ public partial class Eyesight : Area2D
public void OnAreaEntered(Area2D area) public void OnAreaEntered(Area2D area)
{ {
var entity = area.GetParent<IEntity>(); var entity = area.GetParent<Entity>();
if (entity != null) if (entity != null)
{ {
_detectedEntities.Add(entity); _detectedEntities.Add(entity);
@ -30,7 +30,7 @@ public partial class Eyesight : Area2D
public void OnAreaExited(Area2D area) public void OnAreaExited(Area2D area)
{ {
var entity = area.GetParent<IEntity>(); var entity = area.GetParent<Entity>();
if (entity != null) if (entity != null)
{ {
if (_detectedEntities.Contains(entity)) if (_detectedEntities.Contains(entity))

View file

@ -4,10 +4,11 @@ namespace Newlon.Components.Plants;
public partial class LoseZone : RuntimePlantData public partial class LoseZone : RuntimePlantData
{ {
public override void TakeDamage(int amount, Node origin, Utility.DamageTypes damageType = Utility.DamageTypes.PHYSICAL) public override void TakeDamage(float amount, Node origin)
{ {
}
}
public override void Kill() public override void Kill()
{ {

View file

@ -11,6 +11,7 @@ public partial class ReturnEffect : Node
public void OnDamageRecieved(int delta,Node origin) public void OnDamageRecieved(int delta,Node origin)
{ {
if (delta >= 0) return;
if (origin is RuntimeZombieData zombie) if (origin is RuntimeZombieData zombie)
{ {
zombie.GiveEffect(_effectToReturn); zombie.GiveEffect(_effectToReturn);

View file

@ -7,64 +7,19 @@ namespace Newlon.Components.Plants;
// Data that plant stores during runtime // Data that plant stores during runtime
// //
public partial class RuntimePlantData : Node2D, IEntity public partial class RuntimePlantData : Entity
{ {
[Export]
private int _maxHP;
private int _hp;
public int Hp => _hp;
public int MaxHp => _maxHP;
public int Line { get; set; } public int Line { get; set; }
public PlantResource Resource; public PlantResource Resource;
private AudioStream eatenSound = ResourceLoader.Load<AudioStream>("res://assets/audio/sfx/gulp.mp3");
private AudioStream eatenSound; public override void KillByDamage()
[Signal]
public delegate void OnHPChangedEventHandler(int amount, Node origin);
public override void _Ready()
{ {
_hp = _maxHP; AudioSequencer.Play("plant_eaten", eatenSound);
eatenSound = ResourceLoader.Load<AudioStream>("res://assets/audio/sfx/gulp.mp3"); base.KillByDamage();
} }
public override void Kill()
public virtual void Heal(int amount, Node origin)
{
_hp += amount;
EmitSignal(SignalName.OnHPChanged, amount, origin);
if (MaxHp > 0)
{
_hp = MaxHp;
}
}
public virtual void TakeDamage(int amount, Node origin, Utility.DamageTypes damageType = Utility.DamageTypes.PHYSICAL)
{
_hp -= amount;
EmitSignal(SignalName.OnHPChanged, -amount, origin);
if (_hp <= 0)
{
Kill();
AudioSequencer.Play("plant_eaten", eatenSound);
}
}
public virtual void Kill()
{ {
PoolContainer.Instance.EntityField[Resource.Layer].Remove(GlobalPosition); PoolContainer.Instance.EntityField[Resource.Layer].Remove(GlobalPosition);
QueueFree(); QueueFree();
} }
public virtual void DisableBrain()
{
GetNode<Node>("Behaviour").ProcessMode = ProcessModeEnum.Disabled;
}
public virtual void EnableBrain()
{
GetNode<Node>("Behaviour").ProcessMode = ProcessModeEnum.Inherit;
}
} }

View file

@ -22,7 +22,7 @@ public partial class AloeBehaviour : BaseBehaviour
var checkPos = GetParent<Node2D>().GlobalPosition + Vector2.Right * Utility.TileWidth; var checkPos = GetParent<Node2D>().GlobalPosition + Vector2.Right * Utility.TileWidth;
if(_charge && PoolContainer.Instance.TryGetEntity(checkPos, out RuntimePlantData plantData)) if(_charge && PoolContainer.Instance.TryGetEntity(checkPos, out RuntimePlantData plantData))
{ {
if((float)plantData.Hp / (float)plantData.MaxHp < _hpTreshold) if((float)plantData.HP / (float)plantData.MaxHP < _hpTreshold)
{ {
_charge = false; _charge = false;
_tree.Set("parameters/conditions/heal",true); _tree.Set("parameters/conditions/heal",true);
@ -36,7 +36,7 @@ public partial class AloeBehaviour : BaseBehaviour
var checkPos = GetParent<Node2D>().GlobalPosition + Vector2.Right * Utility.TileWidth; var checkPos = GetParent<Node2D>().GlobalPosition + Vector2.Right * Utility.TileWidth;
if (PoolContainer.Instance.TryGetEntity(checkPos, out RuntimePlantData plantData)) if (PoolContainer.Instance.TryGetEntity(checkPos, out RuntimePlantData plantData))
{ {
plantData.Heal(3000 + 25 * plantData.MaxHp, GetParent()); plantData.Heal(3000 + 25 * plantData.MaxHP, GetParent());
} }
} }

View file

@ -13,6 +13,6 @@ public partial class HpBasedBehaviour : BaseBehaviour
public void OnHPChanged(int amount,Node origin) public void OnHPChanged(int amount,Node origin)
{ {
_tree.Set("parameters/blend_position",(float)_data.Hp/_data.MaxHp); _tree.Set("parameters/blend_position",(float)_data.HP/_data.MaxHP);
} }
} }

View file

@ -1,162 +1,48 @@
using System.Collections.Generic;
using Godot; using Godot;
using Newlon.Systems.Effects;
namespace Newlon.Components.Zombies; namespace Newlon.Components.Zombies;
public partial class RuntimeZombieData : Node2D, IEntity, ILocalTimescale, IEffectHandler public partial class RuntimeZombieData : Entity
{ {
[Signal]
public delegate void OnHPChangedEventHandler(int deltaHP, Node origin);
[Signal]
public delegate void OnDamagedEventHandler();
[Signal]
public delegate void OnLocalTimescaleChangedEventHandler(int currentTimescale);
[Export]
private int _hp;
[Export]
private int _maxHP;
[Export] [Export]
private Armor _armor; private Armor _armor;
[Export] [Export]
private AudioStream garlicSound; private AudioStream garlicSound;
[Export] [Export]
private AudioStream freezeSound; private AudioStream freezeSound;
private float _localTimescale = 1.0f;
public int Hp => _hp;
public int MaxHp => _maxHP;
public bool AbleToEat = true; public bool AbleToEat = true;
public float LocalTimescale public override void Heal(float amount, Node origin)
{
get => _localTimescale;
set
{
_localTimescale = value;
EmitSignal(SignalName.OnLocalTimescaleChanged,_localTimescale);
}
}
public override void _Ready()
{
_hp = _maxHP;
// Effect timers setup
for(int i = 0; i < Utility.EffectSlotCount; i++)
{
var timer = new Timer() {Autostart = false, OneShot = true};
_effectSlotTimers[i] = timer;
AddChild(timer);
int current_index = i;
timer.Timeout += () => {EndEffectAtSlot(current_index);};
}
}
public void Heal(int amount,Node origin)
{
if(_armor != null)
{
_hp += _armor.Heal(amount);
}
else
_hp += amount;
EmitSignal(SignalName.OnHPChanged,amount,origin);
if (MaxHp > 0)
{
_hp = MaxHp;
}
}
public void TakeDamage(int amount, Node origin, Utility.DamageTypes damageType = Utility.DamageTypes.PHYSICAL)
{ {
if (_armor != null) if (_armor != null)
{ {
_hp -= _armor.RecieveDamage(amount); HP += _armor.Heal(amount);
} }
else else
_hp -= amount; HP += amount;
EmitSignal(SignalName.OnHPChanged,-amount, origin); EmitSignal(SignalName.OnHPChanged, amount, origin);
if (HP > MaxHP)
{
HP = MaxHP;
}
}
public override void TakeDamage(float amount, Node origin)
{
if (_armor != null)
{
HP -= _armor.RecieveDamage(amount);
}
else
HP -= amount;
EmitSignal(SignalName.OnHPChanged, -amount, origin);
EmitSignal(SignalName.OnDamaged); EmitSignal(SignalName.OnDamaged);
if (_hp <= 0) if (HP <= 0)
{ {
QueueFree(); KillByDamage();
} }
} }
#region Effects system
private readonly Effect[] _activeEffectSlots = new Effect[Utility.EffectSlotCount];
private readonly Timer[] _effectSlotTimers = new Timer[Utility.EffectSlotCount];
// Effect handling
public void GiveEffect(Effect what)
{
int slot = (int)what.Slot;
if (_activeEffectSlots[slot] == null)
{
switch (what.Slot)
{
case Utility.EffectSlots.FREEZE:
AudioSequencer.Play("zombie_freeze", freezeSound);
var settings = new ChannelSettings();
settings.restartTreshold = -1;
AudioSequencer.ChangeSettings("zombie_freeze",settings);
break;
case Utility.EffectSlots.GARLIC:
AudioSequencer.Play("zombie_garlic", garlicSound);
break;
}
}
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;
}
public void EndEffect(Effect what)
{
what.Exit(this);
_activeEffectSlots[(int)what.Slot] = null;
}
public void ProcessEffects()
{
for(int i = 0; i < Utility.EffectSlotCount; i++)
_activeEffectSlots[i]?.Process(this);
}
private void EndEffectAtSlot(int slot)
{
_activeEffectSlots[slot].Exit(this);
_activeEffectSlots[slot] = null;
}
public void DisableBrain()
{
GetNode<AnimationPlayer>("AnimationPlayer").ProcessMode = ProcessModeEnum.Pausable;
ProcessMode = ProcessModeEnum.Disabled;
}
public void EnableBrain()
{
GetNode<AnimationPlayer>("AnimationPlayer").ProcessMode = ProcessModeEnum.Inherit;
ProcessMode = ProcessModeEnum.Inherit;
}
#endregion
} }

View file

@ -5,7 +5,7 @@ namespace Newlon.Systems.Effects;
public abstract partial class Effect : Resource public abstract partial class Effect : Resource
{ {
[Export] public float Duration; [Export] public float Duration;
[Export] public Utility.EffectSlots Slot; [Export] public string Slot;
public abstract void Enter(Node target); public abstract void Enter(Node target);
public abstract void Process(Node target); public abstract void Process(Node target);
public abstract void Exit(Node target); public abstract void Exit(Node target);

View file

@ -1,8 +0,0 @@
namespace Newlon.Systems.Effects;
public interface IEffectHandler
{
void GiveEffect(Effect what);
void EndEffect(Effect what);
void ProcessEffects();
}

View file

@ -1 +0,0 @@
uid://eeqk3fvjwg31

View file

@ -11,21 +11,20 @@ public partial class SlownessEffect : Effect
public override void Enter(Node target) public override void Enter(Node target)
{ {
if (target is IEffectHandler handler) if (target is Entity entity)
{ {
if (target is ILocalTimescale timescalable) entity.LocalTimescale *= Multiplier;
timescalable.LocalTimescale *= Multiplier; entity.Modulate = ColorOverride;
if (target is CanvasItem canvasItem)
canvasItem.Modulate = ColorOverride;
} }
} }
public override void Exit(Node target) public override void Exit(Node target)
{ {
if(target is ILocalTimescale timescalable) if (target is Entity entity)
timescalable.LocalTimescale /= Multiplier; {
if(target is CanvasItem canvasItem) entity.LocalTimescale /= Multiplier;
canvasItem.Modulate = Colors.White; entity.Modulate = Colors.White;
}
} }
public override void Process(Node target) public override void Process(Node target)

View file

@ -35,12 +35,14 @@ Attack damage: [color=darkred]1 bite[/color]. ","Бронированная ве
Урон от атаки: [color=darkred]1 укус[/color]." Урон от атаки: [color=darkred]1 укус[/color]."
hobo,Trash can zombie,Зомби в мусорке hobo,Trash can zombie,Зомби в мусорке
hobo_desc,"Some time in the past he met a great conqueror. Hates to live without his can. Equivalent of ~8 zombies. hobo_desc,"Some time in the past he met a great conqueror. Hates to live without his can. Equivalent of ~8 zombies.
[color=darkgreen]He is immune to garlic-like effects. [/color]
Health points: [color=darkred]13 peas[/color]. Health points: [color=darkred]13 peas[/color].
Armour points: [color=darkred]39 peas[/color]. Armour points: [color=darkred]39 peas[/color].
Walk speed: [color=darkred]0.2 tiles/second[/color]. Walk speed: [color=darkred]0.2 tiles/second[/color].
Run speed: [color=darkred]0.6 tiles/second[/color]. Run speed: [color=darkred]0.6 tiles/second[/color].
Attack speed: [color=darkred]1 Hz[/color]. Attack speed: [color=darkred]1 Hz[/color].
Attack damage: [color=darkred]1 bite[/color]. ","Однажды повстречал великого полководца. Ненавидит жить без своего ведра. Равен примерно 8 зомби. Attack damage: [color=darkred]1 bite[/color]. ","Однажды повстречал великого полководца. Ненавидит жить без своего ведра. Равен примерно 8 зомби.
[color=darkgreen]Имеет иммунитет к эффектам как у чеснока. [/color]
Очки здоровья: [color=darkred]13 горошин[/color]. Очки здоровья: [color=darkred]13 горошин[/color].
Armour points: [color=darkred]39 горошин[/color]. Armour points: [color=darkred]39 горошин[/color].
Скорость ходьбы: [color=darkred]0.2 клеток/сек[/color]. Скорость ходьбы: [color=darkred]0.2 клеток/сек[/color].

1 keys en ru
35
36
37
38
39
40
41
42
43
44
45
46
47
48

Binary file not shown.

Binary file not shown.