diff --git a/Assets/Sprites/sword.ase b/Assets/Sprites/sword.ase
index d8c0d01..1826a21 100644
Binary files a/Assets/Sprites/sword.ase and b/Assets/Sprites/sword.ase differ
diff --git a/Assets/Sprites/sword.png b/Assets/Sprites/sword.png
index 076ccbf..546593d 100644
Binary files a/Assets/Sprites/sword.png and b/Assets/Sprites/sword.png differ
diff --git a/Items/Weapon.cs b/Items/Weapon.cs
index 33476f6..c926d38 100644
--- a/Items/Weapon.cs
+++ b/Items/Weapon.cs
@@ -35,16 +35,10 @@ namespace SupaLidlGame.Items
[Export]
public float InitialVelocity { get; set; } = 0;
- ///
- /// Whether or not the weapon can parry other weapons and is
- /// parryable by other weapons.
- ///
public virtual bool IsParryable { get; protected set; } = false;
public bool IsParried { get; set; }
- public ulong ParryTimeOrigin { get; protected set; }
-
public Character Character { get; set; }
[Export]
@@ -53,6 +47,9 @@ namespace SupaLidlGame.Items
[Export]
public float MaxDistanceHint { get; set; }
+ [Export]
+ public Node2D Anchor { get; set; }
+
public override bool StacksWith(Item item) => false;
public override void Equip(Character character)
diff --git a/Items/Weapons/IParryable.cs b/Items/Weapons/IParryable.cs
new file mode 100644
index 0000000..49b9a4c
--- /dev/null
+++ b/Items/Weapons/IParryable.cs
@@ -0,0 +1,10 @@
+namespace SupaLidlGame.Items.Weapons
+{
+ public interface IParryable
+ {
+ public bool IsParryable { get; }
+ public bool IsParried { get; }
+ public ulong ParryTimeOrigin { get; }
+ public void Stun();
+ }
+}
diff --git a/Items/Weapons/Sword.cs b/Items/Weapons/Sword.cs
index c399943..9b23b70 100644
--- a/Items/Weapons/Sword.cs
+++ b/Items/Weapons/Sword.cs
@@ -2,19 +2,26 @@ using Godot;
using SupaLidlGame.BoundingBoxes;
using SupaLidlGame.Characters;
using SupaLidlGame.Extensions;
+using SupaLidlGame.State.Sword;
namespace SupaLidlGame.Items.Weapons
{
- public partial class Sword : Weapon
+ public partial class Sword : Weapon, IParryable
{
public bool IsAttacking { get; protected set; }
+ public override bool IsUsing => StateMachine.CurrentState
+ is SwordAttackState;
+
[Export]
public Hitbox Hitbox { get; set; }
[Export]
public AnimationPlayer AnimationPlayer { get; set; }
+ [Export]
+ public AnimationTree AnimationTree { get; set; }
+
///
/// The time frame in seconds for which the weapon will deal damage.
///
@@ -25,29 +32,25 @@ namespace SupaLidlGame.Items.Weapons
[Export]
public double AttackTime { get; set; } = 0;
+ [Export]
+ public double AttackAnimationDuration { get; set; }
+
[Export]
public CpuParticles2D ParryParticles { get; set; }
+ [Export]
+ public double NPCAnticipateTime { get; set; }
+
+ [Export]
+ public SwordStateMachine StateMachine { get; set; }
+
public override bool IsParryable { get; protected set; }
- [Export]
- public float AnticipationAngle { get; set; }
+ public ulong ParryTimeOrigin { get; protected set; }
- [Export]
- public float OvershootAngle { get; set; }
-
- [Export]
- public float RecoveryAngle { get; set; }
-
- [Export]
- public float AnticipationDuration { get; set; }
-
- [Export]
- public float OvershootDuration { get; set; }
-
- [Export]
- public float RecoveryDuration { get; set; }
+ private Tween _currentTween;
+ private AnimationNodeStateMachinePlayback _playback;
public override void Equip(Character character)
{
@@ -67,17 +70,22 @@ namespace SupaLidlGame.Items.Weapons
// we can't use if we're still using the weapon
if (RemainingUseTime > 0)
{
- return;
+ //return;
}
+ StateMachine.Use();
+
+ /*
// reset state of the weapon
IsParried = false;
IsParryable = true;
ParryTimeOrigin = Time.GetTicksMsec();
- AnimationPlayer.Stop();
+ _playback.Travel("use");
+ */
// play animation depending on rotation of weapon
+ /*
string anim = "use";
if (GetNode("Anchor").Rotation > Mathf.DegToRad(50))
@@ -92,10 +100,24 @@ namespace SupaLidlGame.Items.Weapons
}
AnimationPlayer.Play(anim);
+ */
base.Use();
}
+ public void EnableParry()
+ {
+ IsParried = false;
+ IsParryable = true;
+ ParryTimeOrigin = Time.GetTicksMsec();
+ GD.Print(Character.Name);
+ }
+
+ public void DisableParry()
+ {
+ IsParryable = false;
+ }
+
public override void Deuse()
{
//AnimationPlayer.Stop();
@@ -113,7 +135,7 @@ namespace SupaLidlGame.Items.Weapons
public void Deattack()
{
IsAttacking = false;
- IsParryable = false;
+ DisableParry();
Hitbox.IsDisabled = true;
ProcessHits();
Hitbox.ResetIgnoreList();
@@ -123,10 +145,13 @@ namespace SupaLidlGame.Items.Weapons
public override void _Ready()
{
Hitbox.Damage = Damage;
+ _playback = (AnimationNodeStateMachinePlayback)AnimationTree
+ .Get("parameters/playback");
}
public override void _Process(double delta)
{
+ StateMachine.Process(delta);
base._Process(delta);
}
@@ -149,23 +174,31 @@ namespace SupaLidlGame.Items.Weapons
public void AttemptParry(Weapon otherWeapon)
{
- if (IsParryable && otherWeapon.IsParryable)
+ //if (IsParryable && otherWeapon.IsParryable)
+ if (otherWeapon.IsParryable &&
+ otherWeapon is IParryable otherParryable)
{
ParryParticles.Emitting = true;
- if (ParryTimeOrigin < otherWeapon.ParryTimeOrigin)
+ if (ParryTimeOrigin < otherParryable.ParryTimeOrigin)
{
// our character was parried
- IsParried = true;
- AnimationPlayer.SpeedScale = 0.25f;
- Character.Stun(1.5f);
- GD.Print(ParryTimeOrigin);
- GD.Print(otherWeapon.ParryTimeOrigin);
- GetNode("ParrySound").OnWorld().Play();
+ }
+ else
+ {
+ otherParryable.Stun();
}
}
//this.GetAncestor().AddChild(instance);
}
+ public void Stun()
+ {
+ IsParried = true;
+ AnimationPlayer.SpeedScale = 0.25f;
+ Character.Stun(1.5f);
+ GetNode("ParrySound").OnWorld().Play();
+ }
+
public override void _on_hitbox_hit(BoundingBox box)
{
if (IsParried)
@@ -194,5 +227,10 @@ namespace SupaLidlGame.Items.Weapons
}
}
}
+
+ protected void SetAnimationCondition(string condition, bool value)
+ {
+ AnimationTree.Set("parameters/conditions/" + condition, value);
+ }
}
}
diff --git a/Items/Weapons/Sword.tscn b/Items/Weapons/Sword.tscn
index d2c61e8..9f35ed3 100644
--- a/Items/Weapons/Sword.tscn
+++ b/Items/Weapons/Sword.tscn
@@ -1,12 +1,21 @@
-[gd_scene load_steps=20 format=3 uid="uid://d72ehtv1ks0e"]
+[gd_scene load_steps=35 format=3 uid="uid://d72ehtv1ks0e"]
[ext_resource type="Script" path="res://Items/Weapons/Sword.cs" id="1_mlo73"]
-[ext_resource type="Texture2D" uid="uid://dt6u8p4h6g7le" path="res://Assets/Sprites/knife.png" id="2_rnfo4"]
+[ext_resource type="Script" path="res://State/Sword/SwordStateMachine.cs" id="2_rje2m"]
+[ext_resource type="Script" path="res://State/Sword/SwordIdleState.cs" id="3_qh2cs"]
+[ext_resource type="Texture2D" uid="uid://dp7osg05ip5oo" path="res://Assets/Sprites/sword.png" id="3_r75ni"]
[ext_resource type="PackedScene" uid="uid://du5vhccg75nrq" path="res://BoundingBoxes/Hitbox.tscn" id="3_up3ob"]
[ext_resource type="PackedScene" uid="uid://cojxmcin13ihm" path="res://Utils/Trail.tscn" id="4_pt6lq"]
+[ext_resource type="Script" path="res://State/Sword/SwordAnticipateState.cs" id="4_ycuhw"]
+[ext_resource type="Script" path="res://State/Sword/SwordAttackState.cs" id="5_30003"]
[ext_resource type="Texture2D" uid="uid://do1bui3bblkk7" path="res://Assets/Sprites/sword-swing.png" id="5_pywek"]
[ext_resource type="AudioStream" uid="uid://c4n7ioxpukdwi" path="res://Assets/Sounds/parry.wav" id="6_8nxjm"]
+[sub_resource type="Environment" id="Environment_72txp"]
+background_mode = 3
+glow_enabled = true
+glow_hdr_threshold = 1.42
+
[sub_resource type="Curve" id="Curve_4cxtp"]
_data = [Vector2(0.00687286, 1), 0.0, 0.0, 0, 0, Vector2(0.879725, 0.190909), -2.93145, -2.93145, 0, 0, Vector2(1, 0.0454545), 0.0483926, 0.0, 0, 0]
point_count = 3
@@ -30,8 +39,8 @@ tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
-"update": 0,
-"values": [-0.610865]
+"update": 1,
+"values": [-1.5708]
}
tracks/1/type = "value"
tracks/1/imported = false
@@ -45,151 +54,94 @@ tracks/1/keys = {
"update": 1,
"values": [0]
}
+tracks/2/type = "value"
+tracks/2/imported = false
+tracks/2/enabled = true
+tracks/2/path = NodePath("Anchor:position")
+tracks/2/interp = 1
+tracks/2/loop_wrap = true
+tracks/2/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"update": 1,
+"values": [Vector2(0, 0)]
+}
+tracks/3/type = "value"
+tracks/3/imported = false
+tracks/3/enabled = true
+tracks/3/path = NodePath("Anchor/Node2D:rotation")
+tracks/3/interp = 1
+tracks/3/loop_wrap = true
+tracks/3/keys = {
+"times": PackedFloat32Array(0, 0.0001, 0.0002),
+"transitions": PackedFloat32Array(1, 1, 1),
+"update": 1,
+"values": [0.0, 0.0, 0.0]
+}
+tracks/4/type = "method"
+tracks/4/imported = false
+tracks/4/enabled = true
+tracks/4/path = NodePath(".")
+tracks/4/interp = 1
+tracks/4/loop_wrap = true
+tracks/4/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"values": [{
+"args": ["is_alternate", false],
+"method": &"SetAnimationCondition"
+}]
+}
-[sub_resource type="Animation" id="Animation_r58x0"]
+[sub_resource type="Animation" id="Animation_ameas"]
+resource_name = "anticipate"
+length = 0.1
+tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("Anchor:rotation")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(0.933033),
+"update": 1,
+"values": [-2.35619]
+}
+
+[sub_resource type="Animation" id="Animation_bj2ky"]
+resource_name = "anticipate_alternate"
+length = 0.1
+tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("Anchor:rotation")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"update": 1,
+"values": [2.35619]
+}
+tracks/1/type = "value"
+tracks/1/imported = false
+tracks/1/enabled = true
+tracks/1/path = NodePath("Anchor/Node2D:rotation")
+tracks/1/interp = 1
+tracks/1/loop_wrap = true
+tracks/1/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"update": 0,
+"values": [3.14159]
+}
+
+[sub_resource type="Animation" id="Animation_b8r8j"]
+resource_name = "anticipate_bot"
+
+[sub_resource type="Animation" id="Animation_6jphj"]
resource_name = "attack"
-length = 1.5
-step = 0.05
-tracks/0/type = "value"
-tracks/0/imported = false
-tracks/0/enabled = true
-tracks/0/path = NodePath("Anchor:rotation")
-tracks/0/interp = 1
-tracks/0/loop_wrap = false
-tracks/0/keys = {
-"times": PackedFloat32Array(0.05, 0.1, 0.2, 0.25, 0.75, 1.5),
-"transitions": PackedFloat32Array(1, 4, 1, 2, 4, 1),
-"update": 3,
-"values": [-0.610865, -0.959931, 3.92699, 3.92699, 3.75246, -0.785398]
-}
-tracks/1/type = "method"
-tracks/1/imported = false
-tracks/1/enabled = true
-tracks/1/path = NodePath(".")
-tracks/1/interp = 1
-tracks/1/loop_wrap = true
-tracks/1/keys = {
-"times": PackedFloat32Array(0.1, 0.25),
-"transitions": PackedFloat32Array(1, 1),
-"values": [{
-"args": [],
-"method": &"Attack"
-}, {
-"args": [],
-"method": &"Deattack"
-}]
-}
-tracks/2/type = "value"
-tracks/2/imported = false
-tracks/2/enabled = true
-tracks/2/path = NodePath("SwingSprite:frame")
-tracks/2/interp = 1
-tracks/2/loop_wrap = true
-tracks/2/keys = {
-"times": PackedFloat32Array(0, 0.2, 0.3, 0.4),
-"transitions": PackedFloat32Array(1, 1, 1, 1),
-"update": 1,
-"values": [0, 1, 2, 0]
-}
-
-[sub_resource type="Animation" id="Animation_mv7y2"]
-resource_name = "idle"
-
-[sub_resource type="Animation" id="Animation_orc8t"]
-resource_name = "use"
-length = 1.5
-step = 0.05
-tracks/0/type = "value"
-tracks/0/imported = false
-tracks/0/enabled = true
-tracks/0/path = NodePath("Anchor:rotation")
-tracks/0/interp = 1
-tracks/0/loop_wrap = false
-tracks/0/keys = {
-"times": PackedFloat32Array(0.05, 0.1, 0.2, 0.25, 0.75, 1.5),
-"transitions": PackedFloat32Array(1, 4, 1, 2, 4, 1),
-"update": 3,
-"values": [-0.610865, -0.959931, 3.92699, 3.92699, 3.75246, -0.785398]
-}
-tracks/1/type = "method"
-tracks/1/imported = false
-tracks/1/enabled = true
-tracks/1/path = NodePath(".")
-tracks/1/interp = 1
-tracks/1/loop_wrap = true
-tracks/1/keys = {
-"times": PackedFloat32Array(0.1, 0.25),
-"transitions": PackedFloat32Array(1, 1),
-"values": [{
-"args": [],
-"method": &"Attack"
-}, {
-"args": [],
-"method": &"Deattack"
-}]
-}
-tracks/2/type = "value"
-tracks/2/imported = false
-tracks/2/enabled = true
-tracks/2/path = NodePath("SwingSprite:frame")
-tracks/2/interp = 1
-tracks/2/loop_wrap = true
-tracks/2/keys = {
-"times": PackedFloat32Array(0, 0.2, 0.3, 0.4),
-"transitions": PackedFloat32Array(1, 1, 1, 1),
-"update": 1,
-"values": [0, 1, 2, 0]
-}
-
-[sub_resource type="Animation" id="Animation_nivo8"]
-resource_name = "use-npc"
-length = 1.5
-step = 0.05
-tracks/0/type = "value"
-tracks/0/imported = false
-tracks/0/enabled = true
-tracks/0/path = NodePath("Anchor:rotation")
-tracks/0/interp = 1
-tracks/0/loop_wrap = false
-tracks/0/keys = {
-"times": PackedFloat32Array(0.05, 0.3, 0.4, 0.45, 0.75, 1.5),
-"transitions": PackedFloat32Array(1, 4, 1, 2, 4, 1),
-"update": 3,
-"values": [-0.610865, -1.309, 3.92699, 3.92699, 3.75246, -0.785398]
-}
-tracks/1/type = "method"
-tracks/1/imported = false
-tracks/1/enabled = true
-tracks/1/path = NodePath(".")
-tracks/1/interp = 1
-tracks/1/loop_wrap = true
-tracks/1/keys = {
-"times": PackedFloat32Array(0.3, 0.45),
-"transitions": PackedFloat32Array(1, 1),
-"values": [{
-"args": [],
-"method": &"Attack"
-}, {
-"args": [],
-"method": &"Deattack"
-}]
-}
-tracks/2/type = "value"
-tracks/2/imported = false
-tracks/2/enabled = true
-tracks/2/path = NodePath("SwingSprite:frame")
-tracks/2/interp = 1
-tracks/2/loop_wrap = true
-tracks/2/keys = {
-"times": PackedFloat32Array(0, 0.4, 0.5, 0.6),
-"transitions": PackedFloat32Array(1, 1, 1, 1),
-"update": 1,
-"values": [0, 1, 2, 0]
-}
-
-[sub_resource type="Animation" id="Animation_y4mj3"]
-resource_name = "use2"
-length = 0.75
step = 0.05
tracks/0/type = "value"
tracks/0/imported = false
@@ -198,44 +150,61 @@ tracks/0/path = NodePath("Anchor:rotation")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
-"times": PackedFloat32Array(0.05, 0.1, 0.2, 0.25, 0.75),
-"transitions": PackedFloat32Array(1, 4, 1, 2, 1),
-"update": 3,
-"values": [3.75246, 4.10152, -0.785398, -0.785398, -0.610865]
+"times": PackedFloat32Array(0, 0.15, 0.35),
+"transitions": PackedFloat32Array(1, 1, 1),
+"update": 1,
+"values": [-1.5708, 1.5708, 1.5708]
}
-tracks/1/type = "method"
+tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
-tracks/1/path = NodePath(".")
+tracks/1/path = NodePath("SwingSprite:frame")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
-"times": PackedFloat32Array(0.1, 0.25),
-"transitions": PackedFloat32Array(1, 1),
-"values": [{
-"args": [],
-"method": &"Attack"
-}, {
-"args": [],
-"method": &"Deattack"
-}]
+"times": PackedFloat32Array(0.1, 0.2, 0.4),
+"transitions": PackedFloat32Array(1, 1, 1),
+"update": 1,
+"values": [1, 2, 0]
}
tracks/2/type = "value"
tracks/2/imported = false
-tracks/2/enabled = true
-tracks/2/path = NodePath("SwingSprite:frame")
+tracks/2/enabled = false
+tracks/2/path = NodePath("Anchor:position")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
-"times": PackedFloat32Array(0, 0.2, 0.3, 0.4),
-"transitions": PackedFloat32Array(1, 1, 1, 1),
+"times": PackedFloat32Array(0, 0.1, 0.35),
+"transitions": PackedFloat32Array(1, 1, 1),
"update": 1,
-"values": [0, 1, 3, 0]
+"values": [Vector2(0, 0), Vector2(0, -4), Vector2(0, 0)]
+}
+tracks/3/type = "value"
+tracks/3/imported = false
+tracks/3/enabled = true
+tracks/3/path = NodePath("Anchor/Node2D:rotation")
+tracks/3/interp = 1
+tracks/3/loop_wrap = true
+tracks/3/keys = {
+"times": PackedFloat32Array(0, 0.15),
+"transitions": PackedFloat32Array(1, 1),
+"update": 1,
+"values": [0.0, 2.35619]
+}
+tracks/4/type = "method"
+tracks/4/imported = false
+tracks/4/enabled = true
+tracks/4/path = NodePath(".")
+tracks/4/interp = 1
+tracks/4/loop_wrap = true
+tracks/4/keys = {
+"times": PackedFloat32Array(),
+"transitions": PackedFloat32Array(),
+"values": []
}
-[sub_resource type="Animation" id="Animation_2k2er"]
-resource_name = "use2-npc"
-length = 0.75
+[sub_resource type="Animation" id="Animation_pclfs"]
+resource_name = "attack_alternate"
step = 0.05
tracks/0/type = "value"
tracks/0/imported = false
@@ -244,71 +213,168 @@ tracks/0/path = NodePath("Anchor:rotation")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
-"times": PackedFloat32Array(0.05, 0.3, 0.4, 0.45, 0.75),
-"transitions": PackedFloat32Array(1, 4, 1, 2, 1),
-"update": 3,
-"values": [3.75246, 4.45059, -0.785398, -0.785398, -0.610865]
+"times": PackedFloat32Array(0, 0.15, 0.35),
+"transitions": PackedFloat32Array(1, 1, 1),
+"update": 1,
+"values": [1.5708, -1.5708, -1.5708]
}
-tracks/1/type = "method"
+tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
-tracks/1/path = NodePath(".")
+tracks/1/path = NodePath("SwingSprite:frame")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
-"times": PackedFloat32Array(0.3, 0.45),
-"transitions": PackedFloat32Array(1, 1),
-"values": [{
-"args": [],
-"method": &"Attack"
-}, {
-"args": [],
-"method": &"Deattack"
-}]
+"times": PackedFloat32Array(0.1, 0.2, 0.4),
+"transitions": PackedFloat32Array(1, 1, 1),
+"update": 1,
+"values": [1, 3, 0]
}
tracks/2/type = "value"
tracks/2/imported = false
-tracks/2/enabled = true
-tracks/2/path = NodePath("SwingSprite:frame")
+tracks/2/enabled = false
+tracks/2/path = NodePath("Anchor:position")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
-"times": PackedFloat32Array(0, 0.4, 0.5, 0.6),
-"transitions": PackedFloat32Array(1, 1, 1, 1),
+"times": PackedFloat32Array(0, 0.1, 0.35),
+"transitions": PackedFloat32Array(1, 1, 1),
"update": 1,
-"values": [0, 1, 3, 0]
+"values": [Vector2(0, 0), Vector2(0, -4), Vector2(0, 0)]
+}
+tracks/3/type = "value"
+tracks/3/imported = false
+tracks/3/enabled = true
+tracks/3/path = NodePath("Anchor/Node2D:rotation")
+tracks/3/interp = 1
+tracks/3/loop_wrap = true
+tracks/3/keys = {
+"times": PackedFloat32Array(0, 0.15),
+"transitions": PackedFloat32Array(1, 1),
+"update": 1,
+"values": [3.14159, 0.785398]
+}
+tracks/4/type = "method"
+tracks/4/imported = false
+tracks/4/enabled = true
+tracks/4/path = NodePath(".")
+tracks/4/interp = 1
+tracks/4/loop_wrap = true
+tracks/4/keys = {
+"times": PackedFloat32Array(0.2),
+"transitions": PackedFloat32Array(1),
+"values": [{
+"args": ["is_alternate", false],
+"method": &"SetAnimationCondition"
+}]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_tao4k"]
_data = {
"RESET": SubResource("Animation_b7327"),
-"attack": SubResource("Animation_r58x0"),
-"idle": SubResource("Animation_mv7y2"),
-"use": SubResource("Animation_orc8t"),
-"use-npc": SubResource("Animation_nivo8"),
-"use2": SubResource("Animation_y4mj3"),
-"use2-npc": SubResource("Animation_2k2er")
+"anticipate": SubResource("Animation_ameas"),
+"anticipate_alternate": SubResource("Animation_bj2ky"),
+"anticipate_bot": SubResource("Animation_b8r8j"),
+"attack": SubResource("Animation_6jphj"),
+"attack_alternate": SubResource("Animation_pclfs")
}
-[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_1lid1"]
+[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_7lqgd"]
+animation = &"RESET"
-[sub_resource type="ConvexPolygonShape2D" id="ConvexPolygonShape2D_tv0o2"]
-points = PackedVector2Array(-14.142, -14.142, 0, -20, 14.142, -14.142, 20, 0, 14.142, 14.142, 0, 20, -14.142, 14.142, 0, 0)
+[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_0in44"]
+animation = &"anticipate"
-[node name="Sword" type="Node2D" node_paths=PackedStringArray("Hitbox", "AnimationPlayer", "ParryParticles")]
+[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_2wqg6"]
+animation = &"anticipate_bot"
+
+[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_2ei3g"]
+animation = &"use"
+
+[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_esyoj"]
+
+[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_kg3rd"]
+switch_mode = 2
+advance_mode = 2
+
+[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_twtoe"]
+
+[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_73wvy"]
+switch_mode = 2
+advance_mode = 2
+
+[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_4wst0"]
+switch_mode = 2
+advance_mode = 2
+
+[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_2lfol"]
+switch_mode = 2
+advance_mode = 2
+advance_condition = &"is_player"
+
+[sub_resource type="AnimationNodeStateMachine" id="AnimationNodeStateMachine_q4hbp"]
+states/End/position = Vector2(717, 75)
+states/RESET/node = SubResource("AnimationNodeAnimation_7lqgd")
+states/RESET/position = Vector2(275, 190)
+states/Start/position = Vector2(109, 75)
+states/anticipate/node = SubResource("AnimationNodeAnimation_0in44")
+states/anticipate/position = Vector2(275, 75)
+states/anticipate_bot/node = SubResource("AnimationNodeAnimation_2wqg6")
+states/anticipate_bot/position = Vector2(456, 18)
+states/use/node = SubResource("AnimationNodeAnimation_2ei3g")
+states/use/position = Vector2(609, 75)
+transitions = ["Start", "anticipate", SubResource("AnimationNodeStateMachineTransition_esyoj"), "use", "RESET", SubResource("AnimationNodeStateMachineTransition_kg3rd"), "RESET", "anticipate", SubResource("AnimationNodeStateMachineTransition_twtoe"), "anticipate", "anticipate_bot", SubResource("AnimationNodeStateMachineTransition_73wvy"), "anticipate_bot", "use", SubResource("AnimationNodeStateMachineTransition_4wst0"), "anticipate", "use", SubResource("AnimationNodeStateMachineTransition_2lfol")]
+graph_offset = Vector2(0, -104.073)
+
+[sub_resource type="AnimationNodeStateMachinePlayback" id="AnimationNodeStateMachinePlayback_o5g2u"]
+
+[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_ic623"]
+radius = 20.0
+height = 48.0
+
+[node name="Node2D" type="Node2D" node_paths=PackedStringArray("Hitbox", "AnimationPlayer", "AnimationTree", "ParryParticles", "StateMachine", "Anchor")]
y_sort_enabled = true
texture_filter = 3
script = ExtResource("1_mlo73")
Hitbox = NodePath("Hitbox")
AnimationPlayer = NodePath("AnimationPlayer")
+AnimationTree = NodePath("AnimationTree")
+AttackTime = 0.2
+AttackAnimationDuration = 1.0
ParryParticles = NodePath("Anchor/Node2D/Sprite2D/ParryParticles")
+NPCAnticipateTime = 0.4
+StateMachine = NodePath("State")
Damage = 20.0
-UseTime = 0.5
+UseTime = 0.4
Knockback = 20.0
+Anchor = NodePath("Anchor")
+
+[node name="State" type="Node" parent="." node_paths=PackedStringArray("InitialState")]
+script = ExtResource("2_rje2m")
+InitialState = NodePath("Idle")
+
+[node name="Idle" type="Node" parent="State" node_paths=PackedStringArray("AnticipateState", "Sword")]
+script = ExtResource("3_qh2cs")
+AnticipateState = NodePath("../Anticipate")
+Sword = NodePath("../..")
+
+[node name="Anticipate" type="Node" parent="State" node_paths=PackedStringArray("Sword", "AttackState")]
+script = ExtResource("4_ycuhw")
+Sword = NodePath("../..")
+AttackState = NodePath("../Attack")
+
+[node name="Attack" type="Node" parent="State" node_paths=PackedStringArray("Sword", "AnticipateState", "IdleState")]
+script = ExtResource("5_30003")
+Sword = NodePath("../..")
+AnticipateState = NodePath("../Anticipate")
+IdleState = NodePath("../Idle")
+
+[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
+environment = SubResource("Environment_72txp")
[node name="Anchor" type="Node2D" parent="."]
y_sort_enabled = true
-rotation = -0.610865
+rotation = -1.5708
[node name="Trail" parent="Anchor" node_paths=PackedStringArray("Tracking") instance=ExtResource("4_pt6lq")]
position = Vector2(2.40734, -0.55655)
@@ -318,12 +384,12 @@ gradient = SubResource("Gradient_2ablm")
Tracking = NodePath("../Node2D/Sprite2D")
[node name="Node2D" type="Node2D" parent="Anchor"]
-rotation = -0.846485
+position = Vector2(8, 0)
[node name="Sprite2D" type="Sprite2D" parent="Anchor/Node2D"]
y_sort_enabled = true
-position = Vector2(0, -12)
-texture = ExtResource("2_rnfo4")
+position = Vector2(0, -8)
+texture = ExtResource("3_r75ni")
[node name="ParryParticles" type="CPUParticles2D" parent="Anchor/Node2D/Sprite2D"]
position = Vector2(-0.221825, -3.12132)
@@ -348,19 +414,25 @@ libraries = {
}
[node name="AnimationTree" type="AnimationTree" parent="."]
-tree_root = SubResource("AnimationNodeAnimation_1lid1")
+tree_root = SubResource("AnimationNodeStateMachine_q4hbp")
anim_player = NodePath("../AnimationPlayer")
active = true
+parameters/playback = SubResource("AnimationNodeStateMachinePlayback_o5g2u")
+parameters/conditions/is_player = false
[node name="Hitbox" parent="." instance=ExtResource("3_up3ob")]
IsDisabled = true
[node name="CollisionShape2D" parent="Hitbox" index="0"]
-shape = SubResource("ConvexPolygonShape2D_tv0o2")
+position = Vector2(4, 0)
+rotation = 1.5708
+shape = SubResource("CapsuleShape2D_ic623")
disabled = true
[node name="SwingSprite" type="Sprite2D" parent="."]
+modulate = Color(2, 2, 2, 1)
texture = ExtResource("5_pywek")
+offset = Vector2(4, 0)
hframes = 4
[node name="SwingSound" type="AudioStreamPlayer2D" parent="."]
diff --git a/State/IState.cs b/State/IState.cs
new file mode 100644
index 0000000..c916b1f
--- /dev/null
+++ b/State/IState.cs
@@ -0,0 +1,26 @@
+namespace SupaLidlGame.State
+{
+ public interface IState where T : IState
+ {
+ ///
+ /// Called when this state is entered
+ ///
+ ///
+ /// This returns a IState in case a state is being
+ /// transitioned to but wants to transition to another state. For
+ /// example, an attack state can return to an idle state, but that idle
+ /// state can override it to the move state immediately when necessary.
+ ///
+ public IState Enter(IState previousState) => null;
+
+ ///
+ /// Called when the Character exits this CharacterState.
+ ///
+ public void Exit(IState nextState)
+ {
+
+ }
+
+ public IState Process(double delta) => null;
+ }
+}
diff --git a/State/Machine.cs b/State/Machine.cs
new file mode 100644
index 0000000..07f284e
--- /dev/null
+++ b/State/Machine.cs
@@ -0,0 +1,90 @@
+using Godot;
+
+namespace SupaLidlGame.Characters.State
+{
+ public partial class Machine : Node
+ {
+ protected Character _character;
+
+ public CharacterState State { get; protected set; }
+
+ [Export]
+ public CharacterState InitialState { get; set; }
+
+ [Export]
+ public Character Character { get; set; }
+
+ [Export]
+ public int DebugLevel { get; set; }
+
+ public override void _Ready()
+ {
+ ChangeState(InitialState);
+ }
+
+ public void ChangeState(CharacterState nextState, bool isProxied = false)
+ {
+ if (nextState is null)
+ return;
+
+ if (DebugLevel >= 2)
+ {
+ if (State is not null)
+ {
+ string proxyNote = isProxied ? " (proxied)" : "";
+ GD.Print($"Transition{proxyNote} {State.Name} -> {nextState.Name}");
+ }
+ }
+
+ if (DebugLevel >= 1)
+ {
+ if (GetNode