implement character staggering; fix #34

issue-templates
John Montagu, the 4th Earl of Sandvich 2024-03-25 21:14:06 -07:00
parent c5e110f92d
commit 371b19c41e
Signed by: sandvich
GPG Key ID: 9A39BE37E602B22D
8 changed files with 147 additions and 14 deletions

View File

@ -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<AnimationPlayer>("Animations/Movement");
HurtAnimation = GetNode<AnimationPlayer>("Animations/Hurt");
StunAnimation = GetNode<AnimationPlayer>("Animations/Stun");
AttackAnimation = GetNode<AnimationPlayer>("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
/// <paramref name="time"/> is less than the <c>Character</c>'s current
/// stun time left, it will have no effect.
/// </summary>
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<GpuParticles2D>("Effects/HurtParticles");
if (hurtParticles is not null)
{

View File

@ -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")

View File

@ -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")

View File

@ -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>("%TargetTracer");
Stats = GetNode<PlayerStats>("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);

View File

@ -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")

View File

@ -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")

View File

@ -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<DoubleValue>("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);
}
}
}

View File

@ -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<DoubleValue>("XP");
Level = GetNode<IntValue>("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);