From 371b19c41e5106260b8690188d6aff5a61125381 Mon Sep 17 00:00:00 2001 From: HumanoidSandvichDispenser Date: Mon, 25 Mar 2024 21:14:06 -0700 Subject: [PATCH] implement character staggering; fix #34 --- Characters/Character.cs | 21 +++++++++-- Characters/DocCenturion.tscn | 13 +++++-- Characters/DocLegionary.tscn | 13 +++++-- Characters/Player.cs | 8 ++--- Characters/Player.tscn | 6 +++- Characters/Weeb.tscn | 13 +++++-- Utils/CharacterStats.cs | 69 ++++++++++++++++++++++++++++++++++++ Utils/PlayerStats.cs | 18 +++++++++- 8 files changed, 147 insertions(+), 14 deletions(-) create mode 100644 Utils/CharacterStats.cs diff --git a/Characters/Character.cs b/Characters/Character.cs index cce8860..67f5769 100644 --- a/Characters/Character.cs +++ b/Characters/Character.cs @@ -26,6 +26,9 @@ public partial class Character : CharacterBody2D, IFaction } } + [Export] + public CharacterStats Stats { get; private set; } + [Signal] public delegate void HealthChangedEventHandler(Events.HealthChangedArgs args); @@ -105,12 +108,19 @@ public partial class Character : CharacterBody2D, IFaction public override void _Ready() { - // TODO: 80+ char line MovementAnimation = GetNode("Animations/Movement"); HurtAnimation = GetNode("Animations/Hurt"); StunAnimation = GetNode("Animations/Stun"); AttackAnimation = GetNode("Animations/Attack"); + if (Stats is not null) + { + Stats.Stagger += (double time) => + { + Stun(time); + }; + } + Hurtbox.ReceivedDamage += OnReceivedDamage; } @@ -207,7 +217,7 @@ public partial class Character : CharacterBody2D, IFaction /// is less than the Character's current /// stun time left, it will have no effect. /// - public virtual void Stun(float time) + public virtual void Stun(double time) { StunTime = Mathf.Max(time, StunTime); } @@ -328,10 +338,17 @@ public partial class Character : CharacterBody2D, IFaction return; } + // update stats float oldHealth = Health; damage = ReceiveDamage(damage, inflictor, knockback, knockbackDir); Health -= damage; + if (Stats is not null) + { + Stats.AddStaggerDamage(damage); + } + + // effects var hurtParticles = GetNode("Effects/HurtParticles"); if (hurtParticles is not null) { diff --git a/Characters/DocCenturion.tscn b/Characters/DocCenturion.tscn index c6bd7e2..33fed77 100644 --- a/Characters/DocCenturion.tscn +++ b/Characters/DocCenturion.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=36 format=3 uid="uid://dhamcei7tfta8"] +[gd_scene load_steps=38 format=3 uid="uid://dhamcei7tfta8"] [ext_resource type="Shader" path="res://Shaders/Flash.gdshader" id="1_msit5"] [ext_resource type="Script" path="res://Characters/Enemy.cs" id="2_pkari"] @@ -8,7 +8,9 @@ [ext_resource type="Script" path="res://State/Thinker/ThinkerStateMachine.cs" id="6_6516i"] [ext_resource type="Script" path="res://State/Thinker/CenturionAttackState.cs" id="7_n2slg"] [ext_resource type="Script" path="res://State/Thinker/IdleState.cs" id="8_5neew"] +[ext_resource type="Script" path="res://Utils/CharacterStats.cs" id="9_bxrs2"] [ext_resource type="Script" path="res://Utils/AnimationManager.cs" id="9_fgnr2"] +[ext_resource type="Script" path="res://Utils/Values/DoubleValue.cs" id="10_b38kt"] [ext_resource type="AnimationLibrary" uid="uid://xs6g84fkepjr" path="res://Assets/Animations/npc_hurt.res" id="10_bbwbd"] [ext_resource type="AnimationLibrary" uid="uid://f1aqhnxndybx" path="res://Assets/Animations/npc_stun.res" id="11_a0f8a"] [ext_resource type="Material" uid="uid://bat28samf7ukd" path="res://Assets/Sprites/Particles/NPCDamageProcessMaterial.tres" id="11_p7yev"] @@ -133,7 +135,7 @@ size = Vector2(12, 8) [sub_resource type="RectangleShape2D" id="RectangleShape2D_1gjgc"] size = Vector2(12, 16) -[node name="Centurion" type="CharacterBody2D" node_paths=PackedStringArray("DefaultSelectedItem", "ThinkerStateMachine", "Sprite", "Inventory", "StateMachine", "Hurtbox")] +[node name="Centurion" type="CharacterBody2D" node_paths=PackedStringArray("DefaultSelectedItem", "ThinkerStateMachine", "Stats", "Sprite", "Inventory", "StateMachine", "Hurtbox")] y_sort_enabled = true texture_filter = 3 material = SubResource("ShaderMaterial_2fq6c") @@ -144,12 +146,19 @@ DefaultSelectedItem = NodePath("Inventory/Sword") ThinkerStateMachine = NodePath("ThinkerStateMachine") Speed = 40.0 Mass = 2.0 +Stats = NodePath("Stats") Health = 80.0 Sprite = NodePath("Sprites/Node2D/Character") Inventory = NodePath("Inventory") StateMachine = NodePath("StateMachine") Hurtbox = NodePath("Hurtbox") +[node name="Stats" type="Node" parent="."] +script = ExtResource("9_bxrs2") + +[node name="StaggerDamage" type="Node" parent="Stats"] +script = ExtResource("10_b38kt") + [node name="StateMachine" type="Node" parent="." node_paths=PackedStringArray("InitialState", "Character")] script = ExtResource("3_2dbgx") InitialState = NodePath("Idle") diff --git a/Characters/DocLegionary.tscn b/Characters/DocLegionary.tscn index d415a1e..d8c66b1 100644 --- a/Characters/DocLegionary.tscn +++ b/Characters/DocLegionary.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=43 format=3 uid="uid://cdj50hb84aujp"] +[gd_scene load_steps=45 format=3 uid="uid://cdj50hb84aujp"] [ext_resource type="Shader" path="res://Shaders/Flash.gdshader" id="1_hvgeb"] [ext_resource type="Script" path="res://Characters/Enemy.cs" id="2_h5w5n"] @@ -12,7 +12,9 @@ [ext_resource type="Script" path="res://Utils/AnimationManager.cs" id="9_ssmee"] [ext_resource type="Script" path="res://State/Thinker/PursueState.cs" id="9_u7gxx"] [ext_resource type="Animation" uid="uid://8e8r3y1imvsx" path="res://Assets/Animations/stun.res" id="10_oplmj"] +[ext_resource type="Script" path="res://Utils/CharacterStats.cs" id="11_6o2tx"] [ext_resource type="Material" uid="uid://bat28samf7ukd" path="res://Assets/Sprites/Particles/NPCDamageProcessMaterial.tres" id="11_qcw5x"] +[ext_resource type="Script" path="res://Utils/Values/DoubleValue.cs" id="12_ds5eh"] [ext_resource type="Texture2D" uid="uid://bd8l8kafb42dt" path="res://Assets/Sprites/Particles/circle.png" id="12_rlelw"] [ext_resource type="Material" uid="uid://2tsvsp45elru" path="res://Assets/Sprites/Particles/NPCDeathParticles.tres" id="13_kgmsk"] [ext_resource type="Texture2D" uid="uid://c1a7lvb4uuwfy" path="res://Assets/Sprites/Particles/circle-16.png" id="14_88n3w"] @@ -272,7 +274,7 @@ size = Vector2(8, 8) [sub_resource type="RectangleShape2D" id="RectangleShape2D_1gjgc"] size = Vector2(12, 16) -[node name="Legionary" type="CharacterBody2D" node_paths=PackedStringArray("DefaultSelectedItem", "ThinkerStateMachine", "Sprite", "Inventory", "StateMachine", "Hurtbox")] +[node name="Legionary" type="CharacterBody2D" node_paths=PackedStringArray("DefaultSelectedItem", "ThinkerStateMachine", "Stats", "Sprite", "Inventory", "StateMachine", "Hurtbox")] y_sort_enabled = true texture_filter = 3 material = SubResource("ShaderMaterial_2fq6c") @@ -282,6 +284,7 @@ script = ExtResource("2_h5w5n") DefaultSelectedItem = NodePath("Inventory/DocLance") ThinkerStateMachine = NodePath("ThinkerStateMachine") Speed = 56.0 +Stats = NodePath("Stats") Health = 130.0 Sprite = NodePath("Sprites/Node2D/Character") Inventory = NodePath("Inventory") @@ -289,6 +292,12 @@ StateMachine = NodePath("StateMachine") Hurtbox = NodePath("Hurtbox") metadata/_edit_vertical_guides_ = [] +[node name="Stats" type="Node" parent="."] +script = ExtResource("11_6o2tx") + +[node name="StaggerDamage" type="Node" parent="Stats"] +script = ExtResource("12_ds5eh") + [node name="StateMachine" type="Node" parent="." node_paths=PackedStringArray("InitialState", "Character")] script = ExtResource("3_04p3j") InitialState = NodePath("Idle") diff --git a/Characters/Player.cs b/Characters/Player.cs index 8f49e24..1ffd795 100644 --- a/Characters/Player.cs +++ b/Characters/Player.cs @@ -43,7 +43,7 @@ public sealed partial class Player : Character public AnimationTree AnimationTree { get; private set; } [Export] - public PlayerStats Stats { get; private set; } + public new PlayerStats Stats { get; private set; } public InteractionRay InteractionRay { get; private set; } @@ -57,10 +57,10 @@ public sealed partial class Player : Character _targetTracer = GetNode("%TargetTracer"); - Stats = GetNode("Stats"); - base._Ready(); + Stats = base.Stats as PlayerStats; + Inventory.UsedItem += (Items.Item item) => { if (item is Items.Weapons.Sword) @@ -133,7 +133,7 @@ public sealed partial class Player : Character base.ModifyVelocity(); } - public override void Stun(float time) + public override void Stun(double time) { base.Stun(time); Camera.Shake(2, 0.8f); diff --git a/Characters/Player.tscn b/Characters/Player.tscn index eae3737..d538cc1 100644 --- a/Characters/Player.tscn +++ b/Characters/Player.tscn @@ -576,7 +576,7 @@ size = Vector2(8, 8) closed = false polygon = PackedVector2Array(-3, 0, 2, 0) -[node name="Player" type="CharacterBody2D" node_paths=PackedStringArray("Camera", "DirectionMarker", "Stats", "Sprite", "Inventory", "StateMachine", "Hurtbox")] +[node name="Player" type="CharacterBody2D" node_paths=PackedStringArray("Camera", "DirectionMarker", "Stats", "Stats", "Sprite", "Inventory", "StateMachine", "Hurtbox")] y_sort_enabled = true texture_filter = 3 material = SubResource("ShaderMaterial_h78y7") @@ -587,6 +587,7 @@ Camera = NodePath("Camera2D") DirectionMarker = NodePath("Direction2D") Stats = NodePath("Stats") Speed = 80.0 +Stats = NodePath("Stats") HandTexture = ExtResource("3_3dqh7") Sprite = NodePath("Sprites/Node2D/Character") Inventory = NodePath("Inventory") @@ -603,6 +604,9 @@ script = ExtResource("5_txl0r") [node name="Level" type="Node" parent="Stats"] script = ExtResource("6_sunc5") +[node name="StaggerDamage" type="Node" parent="Stats"] +script = ExtResource("5_txl0r") + [node name="StateMachine" type="Node" parent="." node_paths=PackedStringArray("InitialState", "Character")] script = ExtResource("5_rgckv") InitialState = NodePath("Idle") diff --git a/Characters/Weeb.tscn b/Characters/Weeb.tscn index 1b1eadb..5dc7194 100644 --- a/Characters/Weeb.tscn +++ b/Characters/Weeb.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=38 format=3 uid="uid://glh1bi8fq0y3"] +[gd_scene load_steps=40 format=3 uid="uid://glh1bi8fq0y3"] [ext_resource type="Script" path="res://Characters/NPC.cs" id="1_7fqw6"] [ext_resource type="Shader" path="res://Shaders/Flash.gdshader" id="1_alo0e"] @@ -18,6 +18,8 @@ [ext_resource type="AnimationLibrary" uid="uid://f1aqhnxndybx" path="res://Assets/Animations/npc_stun.res" id="9_bpu34"] [ext_resource type="Texture2D" uid="uid://bd8l8kafb42dt" path="res://Assets/Sprites/Particles/circle.png" id="9_g45p5"] [ext_resource type="Material" uid="uid://2tsvsp45elru" path="res://Assets/Sprites/Particles/NPCDeathParticles.tres" id="10_8f2hb"] +[ext_resource type="Script" path="res://Utils/CharacterStats.cs" id="10_r68bb"] +[ext_resource type="Script" path="res://Utils/Values/DoubleValue.cs" id="11_hda5d"] [ext_resource type="PackedScene" uid="uid://cjgxyhgcyvsv7" path="res://BoundingBoxes/Hurtbox.tscn" id="11_sj7u0"] [ext_resource type="Texture2D" uid="uid://c1a7lvb4uuwfy" path="res://Assets/Sprites/Particles/circle-16.png" id="11_wp6i2"] [ext_resource type="AudioStream" uid="uid://k6kpdj1kv0jg" path="res://Assets/Sounds/splat.ogg" id="12_iwry7"] @@ -160,13 +162,14 @@ size = Vector2(10, 8) [sub_resource type="RectangleShape2D" id="RectangleShape2D_kyos5"] size = Vector2(12, 16) -[node name="Weeb" type="CharacterBody2D" node_paths=PackedStringArray("DefaultSelectedItem", "ThinkerStateMachine", "Sprite", "Inventory", "StateMachine", "Hurtbox")] +[node name="Weeb" type="CharacterBody2D" node_paths=PackedStringArray("DefaultSelectedItem", "ThinkerStateMachine", "Stats", "Sprite", "Inventory", "StateMachine", "Hurtbox")] material = SubResource("ShaderMaterial_etlnr") collision_layer = 6 collision_mask = 17 script = ExtResource("1_7fqw6") DefaultSelectedItem = NodePath("Inventory/Sword") ThinkerStateMachine = NodePath("ThinkerStateMachine") +Stats = NodePath("Stats") Sprite = NodePath("Sprites/Sprite2D") Inventory = NodePath("Inventory") StateMachine = NodePath("StateMachine") @@ -222,6 +225,12 @@ NPC = NodePath("../..") target_desired_distance = 16.0 debug_enabled = true +[node name="Stats" type="Node" parent="."] +script = ExtResource("10_r68bb") + +[node name="StaggerDamage" type="Node" parent="Stats"] +script = ExtResource("11_hda5d") + [node name="Animations" type="Node" parent="."] script = ExtResource("8_dh32x") diff --git a/Utils/CharacterStats.cs b/Utils/CharacterStats.cs new file mode 100644 index 0000000..36da767 --- /dev/null +++ b/Utils/CharacterStats.cs @@ -0,0 +1,69 @@ +using Godot; + +namespace SupaLidlGame.Utils; + +public partial class CharacterStats : Node +{ + public DoubleValue StaggerDamage { get; set; } + + [Export] + public double StaggerCoefficient { get; set; } = 0.5; + + [Export] + public double StaggerDecayVelocity { get; set; } = 5; + + [Export] + public double MaxStagger { get; set; } = 25; + + [Signal] + public delegate void StaggerEventHandler(double time); + + public bool ShouldStagger => StaggerDamage.Value >= MaxStagger; + + protected bool _shouldDecayStagger; + + protected Timer _staggerDecayTimer; + + public override void _Ready() + { + StaggerDamage = GetNode("StaggerDamage"); + + if (StaggerDamage is null) + { + StaggerDamage = new DoubleValue(); + AddChild(StaggerDamage); + } + + _staggerDecayTimer = new Timer(); + _staggerDecayTimer.Timeout += () => _shouldDecayStagger = true; + _staggerDecayTimer.Stop(); + AddChild(_staggerDecayTimer); + } + + public void AddStaggerDamage(float damage) + { + StaggerDamage.Value += damage * StaggerCoefficient; + GD.Print(StaggerDamage.Value); + if (StaggerDamage.Value >= MaxStagger) + { + GD.Print(StaggerDamage.Value + " >= " + MaxStagger); + EmitSignal(SignalName.Stagger, 1); + } + else + { + GD.Print(StaggerDamage.Value + " < " + MaxStagger); + _shouldDecayStagger = false; + _staggerDecayTimer.Stop(); + _staggerDecayTimer.Start(1); + } + } + + public override void _Process(double delta) + { + if (_shouldDecayStagger) + { + StaggerDamage.Value = Mathf.MoveToward( + StaggerDamage.Value, 0, StaggerDecayVelocity * delta); + } + } +} diff --git a/Utils/PlayerStats.cs b/Utils/PlayerStats.cs index 70f51bb..5c46889 100644 --- a/Utils/PlayerStats.cs +++ b/Utils/PlayerStats.cs @@ -3,7 +3,7 @@ using SupaLidlGame.Events; namespace SupaLidlGame.Utils; -public partial class PlayerStats : Node +public partial class PlayerStats : CharacterStats { public const int MAX_XP_PER_LEVEL = 4; @@ -21,9 +21,23 @@ public partial class PlayerStats : Node public override void _Ready() { + base._Ready(); + XP = GetNode("XP"); Level = GetNode("Level"); + if (XP is null) + { + XP = new DoubleValue(); + AddChild(XP); + } + + if (Level is null) + { + Level = new IntValue(); + AddChild(Level); + } + _xpDecayTimer = new Timer(); _xpDecayTimer.Timeout += () => _shouldDecayXP = true; _xpDecayTimer.Stop(); @@ -67,6 +81,8 @@ public partial class PlayerStats : Node public override void _Process(double delta) { + base._Process(delta); + if (_shouldDecayXP) { XP.Value = Mathf.MoveToward(XP.Value, 1, XPDecayVelocity * delta);