From 2c4275eac11c1b49dade24790b4903d2c6de12fd Mon Sep 17 00:00:00 2001 From: HumanoidSandvichDispenser Date: Sat, 19 Nov 2022 21:21:12 -0800 Subject: [PATCH] sword weapon --- BoundingBoxes/Hitbox.cs | 39 +++++-- BoundingBoxes/Hurtbox.cs | 23 +++- Characters/Character.cs | 91 ++++++++++++++- Characters/Enemy.cs | 10 ++ Characters/ExampleEnemy.tscn | 76 +++++++++++-- Characters/NPC.cs | 30 +++-- Characters/Player.cs | 52 ++++++++- Characters/Player.tscn | 90 +++++++++++++-- Characters/States/CharacterState.cs | 1 + Characters/States/Machine.cs | 3 + Characters/States/NPCIdleState.cs | 6 + Characters/States/NPCMoveState.cs | 6 + Characters/States/PlayerAttackState.cs | 34 ++++-- Characters/States/PlayerIdleState.cs | 3 +- Characters/States/PlayerMoveState.cs | 21 +++- Characters/States/PlayerRollState.cs | 6 +- Characters/States/PlayerState.cs | 52 +++++++++ Items/Inventory.cs | 14 ++- Items/Item.cs | 2 + Items/Weapon.cs | 18 ++- Items/Weapons/Sword.cs | 63 ++++++++++- Items/Weapons/Sword.tscn | 149 +++++++++++++++++++++++-- README.org | 7 ++ Scenes/Level.tscn | 15 ++- Sprites/Characters/forsen.ase | Bin 0 -> 10833 bytes Sprites/Characters/forsen.png | Bin 0 -> 3512 bytes Sprites/Characters/forsen.png.import | 34 ++++++ Tests/HitboxTest.tscn | 1 - Utils/IFaction.cs | 13 +++ Utils/Trail.cs | 21 ++++ Utils/Trail.tscn | 8 ++ project.godot | 10 ++ 32 files changed, 810 insertions(+), 88 deletions(-) create mode 100644 README.org create mode 100644 Sprites/Characters/forsen.ase create mode 100644 Sprites/Characters/forsen.png create mode 100644 Sprites/Characters/forsen.png.import create mode 100644 Utils/IFaction.cs create mode 100644 Utils/Trail.cs create mode 100644 Utils/Trail.tscn diff --git a/BoundingBoxes/Hitbox.cs b/BoundingBoxes/Hitbox.cs index 9ac8ea4..8556dc3 100644 --- a/BoundingBoxes/Hitbox.cs +++ b/BoundingBoxes/Hitbox.cs @@ -1,37 +1,54 @@ using System.Collections.Generic; using Godot; using SupaLidlGame.Characters; +using SupaLidlGame.Utils; namespace SupaLidlGame.BoundingBoxes { - public partial class Hitbox : Area2D + public partial class Hitbox : Area2D, IFaction { private HashSet _ignoreList = new HashSet(); [Export] - public float Damage { get; set; } = 0; + public float Damage { get; set; } + /// + /// Getter/setter for the CollisionShape2D's Disabled property. + /// [Export] - public bool IsEnabled { get; set; } + public bool IsDisabled + { + get => _collisionShape.Disabled; + set => _collisionShape.Disabled = value; + } [Export] public float Knockback { get; set; } + [Export] + public ushort Faction { get; set; } + public Character Inflictor { get; set; } + private CollisionShape2D _collisionShape; + + public override void _Ready() + { + _collisionShape = GetNode("CollisionShape2D"); + } + public void _on_area_entered(Area2D area) { - if (!IsEnabled) - { - return; - } - if (area is Hurtbox hurtbox) { - if (!_ignoreList.Contains(hurtbox)) + // we don't want to hurt teammates + if (Faction != hurtbox.Faction) { - _ignoreList.Add(hurtbox); - hurtbox.InflictDamage(Damage, Inflictor, Knockback); + if (!_ignoreList.Contains(hurtbox)) + { + _ignoreList.Add(hurtbox); + hurtbox.InflictDamage(Damage, Inflictor, Knockback); + } } } } diff --git a/BoundingBoxes/Hurtbox.cs b/BoundingBoxes/Hurtbox.cs index 22fbcea..7917ba9 100644 --- a/BoundingBoxes/Hurtbox.cs +++ b/BoundingBoxes/Hurtbox.cs @@ -1,12 +1,29 @@ using Godot; using SupaLidlGame.Characters; +using SupaLidlGame.Utils; namespace SupaLidlGame.BoundingBoxes { - public partial class Hurtbox : Area2D + public partial class Hurtbox : Area2D, IFaction { [Signal] - public delegate void ReceivedDamageEventHandler(float damage); + public delegate void ReceivedDamageEventHandler( + float damage, + Character inflictor, + float knockback, + Vector2 knockbackOrigin = default, + Vector2 knockbackVector = default); + + [Export] + public ushort Faction { get; set; } + + public override void _Ready() + { + if (Faction == default && GetParent() is IFaction factionEntity) + { + Faction = factionEntity.Faction; + } + } public void InflictDamage( float damage, @@ -16,7 +33,7 @@ namespace SupaLidlGame.BoundingBoxes Vector2 knockbackVector = default) { EmitSignal( - "ReceivedDamage", + SignalName.ReceivedDamage, damage, inflictor, knockback, diff --git a/Characters/Character.cs b/Characters/Character.cs index 97d7b0a..936dffa 100644 --- a/Characters/Character.cs +++ b/Characters/Character.cs @@ -1,8 +1,10 @@ using Godot; +using SupaLidlGame.Items; +using SupaLidlGame.Utils; namespace SupaLidlGame.Characters { - public partial class Character : CharacterBody2D + public partial class Character : CharacterBody2D, IFaction { [Export] public float Speed { get; protected set; } = 32.0f; @@ -30,17 +32,50 @@ namespace SupaLidlGame.Characters public Vector2 Target { get; set; } = Vector2.Zero; - public float Health { get; set; } + public float Health + { + get => _health; + set + { + if (!IsAlive && value < 0) + { + return; + } + + _health = value; + GD.Print(_health); + if (_health <= 0) + { + Die(); + } + } + } + + protected float _health = 100f; + + public bool IsAlive => Health > 0; + + [Export] + public AnimatedSprite2D Sprite { get; set; } + + [Export] + public Inventory Inventory { get; set; } [Export] public State.Machine StateMachine { get; set; } + [Export] + public ushort Faction { get; set; } + public override void _Process(double delta) { if (StateMachine != null) { StateMachine.Process(delta); } + + Sprite.FlipH = Target.x < 0; + DrawTarget(); } public override void _Input(InputEvent @event) @@ -59,6 +94,20 @@ namespace SupaLidlGame.Characters } } + /// + /// Modify the Character's velocity + /// + public virtual void ModifyVelocity() + { + + } + + public virtual void Die() + { + GD.Print("lol died"); + QueueFree(); + } + public void ApplyImpulse(Vector2 impulse, bool resetVelocity = false) { // delta p = F delta t @@ -67,9 +116,45 @@ namespace SupaLidlGame.Characters Velocity += impulse / Mass; } - public void _on_hurtbox_received_damage(float damage) + protected void DrawTarget() + { + Vector2 target = Target; + float angle = Mathf.Atan2(target.y, Mathf.Abs(target.x)); + Vector2 scale = Inventory.Scale; + if (target.x < 0) + { + scale.y = -1; + angle = Mathf.Pi - angle; + } + else + { + scale.y = 1; + } + Inventory.Scale = scale; + Inventory.Rotation = angle; + } + + public void _on_hurtbox_received_damage(float damage, + Character inflictor, + float knockback, + Vector2 knockbackOrigin = default, + Vector2 knockbackVector = default) { Health -= damage; + /* + Vector2 knockbackDir = knockbackVector; + if (knockbackDir == default) + { + if (knockbackOrigin == default) + { + knockbackOrigin = inflictor.GlobalPosition; + } + + knockbackDir = knockbackOrigin.DirectionTo(GlobalPosition); + } + + ApplyImpulse(knockback.) + */ } } } diff --git a/Characters/Enemy.cs b/Characters/Enemy.cs index 093aa24..b33b6e1 100644 --- a/Characters/Enemy.cs +++ b/Characters/Enemy.cs @@ -5,5 +5,15 @@ namespace SupaLidlGame.Characters { public partial class Enemy : NPC { + public override void _Ready() + { + Inventory.SelectedItem = Inventory.GetNode("Sword"); + base._Ready(); + } + + public override void Die() + { + base.Die(); + } } } diff --git a/Characters/ExampleEnemy.tscn b/Characters/ExampleEnemy.tscn index 6444d31..785d6a4 100644 --- a/Characters/ExampleEnemy.tscn +++ b/Characters/ExampleEnemy.tscn @@ -1,25 +1,74 @@ -[gd_scene load_steps=9 format=3 uid="uid://dymwd5ihpwyqm"] +[gd_scene load_steps=19 format=3 uid="uid://dymwd5ihpwyqm"] -[ext_resource type="Script" path="res://Characters/NPC.cs" id="1_4x3dm"] -[ext_resource type="Texture2D" uid="uid://bw052v8ikfget" path="res://icon.svg" id="2_ujqd7"] +[ext_resource type="Script" path="res://Characters/Enemy.cs" id="1_2yopk"] +[ext_resource type="Texture2D" uid="uid://dxymfduyrbuvx" path="res://Sprites/Characters/forsen.png" id="2_jfku3"] [ext_resource type="Script" path="res://Characters/States/Machine.cs" id="3_k4ypw"] [ext_resource type="Script" path="res://Characters/States/NPCIdleState.cs" id="4_8r2qn"] [ext_resource type="Script" path="res://Characters/States/NPCMoveState.cs" id="5_utogm"] [ext_resource type="PackedScene" uid="uid://cjgxyhgcyvsv7" path="res://BoundingBoxes/Hurtbox.tscn" id="6_jo0cg"] +[ext_resource type="Script" path="res://Items/Inventory.cs" id="7_43gq8"] +[ext_resource type="PackedScene" uid="uid://cajlwb67xenfy" path="res://Items/Weapons/Sword.tscn" id="8_s3c8r"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_6d2tf"] +atlas = ExtResource("2_jfku3") +region = Rect2(0, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_bdyma"] +atlas = ExtResource("2_jfku3") +region = Rect2(24, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_0dwbr"] +atlas = ExtResource("2_jfku3") +region = Rect2(48, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_r7fn6"] +atlas = ExtResource("2_jfku3") +region = Rect2(72, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_py8k0"] +atlas = ExtResource("2_jfku3") +region = Rect2(96, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_g3nb2"] +atlas = ExtResource("2_jfku3") +region = Rect2(120, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_jauql"] +atlas = ExtResource("2_jfku3") +region = Rect2(144, 0, 24, 24) + +[sub_resource type="SpriteFrames" id="SpriteFrames_4tm2b"] +animations = [{ +"frames": [SubResource("AtlasTexture_6d2tf"), SubResource("AtlasTexture_bdyma")], +"loop": true, +"name": &"idle", +"speed": 5.0 +}, { +"frames": [SubResource("AtlasTexture_0dwbr"), SubResource("AtlasTexture_r7fn6"), SubResource("AtlasTexture_py8k0"), SubResource("AtlasTexture_g3nb2"), SubResource("AtlasTexture_jauql")], +"loop": true, +"name": &"move", +"speed": 12.0 +}] [sub_resource type="RectangleShape2D" id="RectangleShape2D_uict5"] -size = Vector2(32, 16) +size = Vector2(16, 8) [sub_resource type="RectangleShape2D" id="RectangleShape2D_8lxmf"] -size = Vector2(32, 32) +size = Vector2(16, 24) -[node name="ExampleEnemy" type="CharacterBody2D" node_paths=PackedStringArray("StateMachine")] -script = ExtResource("1_4x3dm") +[node name="ExampleEnemy" type="CharacterBody2D" node_paths=PackedStringArray("Sprite", "Inventory", "StateMachine")] +texture_filter = 3 +y_sort_enabled = true +script = ExtResource("1_2yopk") +Sprite = NodePath("Sprite") +Inventory = NodePath("Inventory") StateMachine = NodePath("StateMachine") +Faction = 2 -[node name="Icon" type="Sprite2D" parent="."] -scale = Vector2(0.25, 0.25) -texture = ExtResource("2_ujqd7") +[node name="Sprite" type="AnimatedSprite2D" parent="."] +frames = SubResource("SpriteFrames_4tm2b") +animation = &"move" +playing = true [node name="CollisionShape2D" type="CollisionShape2D" parent="."] position = Vector2(0, 8) @@ -39,10 +88,17 @@ script = ExtResource("5_utogm") IdleState = NodePath("../Idle") [node name="Hurtbox" parent="." instance=ExtResource("6_jo0cg")] +Faction = 2 [node name="CollisionShape2D" parent="Hurtbox" index="0"] shape = SubResource("RectangleShape2D_8lxmf") +[node name="Inventory" type="Node2D" parent="."] +y_sort_enabled = true +script = ExtResource("7_43gq8") + +[node name="Sword" parent="Inventory" instance=ExtResource("8_s3c8r")] + [connection signal="ReceivedDamage" from="Hurtbox" to="." method="_on_hurtbox_received_damage"] [editable path="Hurtbox"] diff --git a/Characters/NPC.cs b/Characters/NPC.cs index 970957d..62b7213 100644 --- a/Characters/NPC.cs +++ b/Characters/NPC.cs @@ -1,4 +1,5 @@ using Godot; +using SupaLidlGame.Items; using System; namespace SupaLidlGame.Characters @@ -8,7 +9,7 @@ namespace SupaLidlGame.Characters /// /// Time in seconds it takes for the NPC to think FeelsDankCube /// - public const float ThinkTime = 0.25f; + public const float ThinkTime = 0.125f; public float[] Weights => _weights; @@ -71,7 +72,7 @@ namespace SupaLidlGame.Characters Character bestChar = null; foreach (Node node in GetParent().GetChildren()) { - if (node != this && node is Player character) + if (node is Character character && character.Faction != Faction) { float dist = Position.DistanceTo(character.Position); if (dist < bestDist) @@ -95,12 +96,23 @@ namespace SupaLidlGame.Characters Direction = _weightDirs[_bestWeightIdx]; } - private void Think() + protected virtual void Think() { Vector2 pos = FindBestTarget().GlobalPosition; - Vector2 dir = Target = GlobalPosition.DirectionTo(pos); + Target = pos - GlobalPosition;//GlobalPosition.DirectionTo(pos); + Vector2 dir = Target.Normalized(); float dist = GlobalPosition.DistanceSquaredTo(pos); + if (Target.LengthSquared() < 1024) + { + GD.Print("lol"); + if (Inventory.SelectedItem is Weapon weapon) + { + GD.Print("use"); + weapon.Use(); + } + } + for (int i = 0; i < 16; i++) { float directDot = _weightDirs[i].Dot(dir); @@ -122,17 +134,19 @@ namespace SupaLidlGame.Characters // "When will I use math in the real world" Clueful - if (dist > 1024) + if (dist > 4096) { _weights[i] = directDot; } else if (dist > 64) { - float directDotWeighting = (dist - 64) / 960; + //float directDotWeighting = dist / 4096; + //float directDotWeighting = Mathf.Log(dist) / _log1024; + float directDotWeighting = Mathf.Sqrt(dist / 4096); float horizDotWeighting = 1 - directDotWeighting; _weights[i] = (directDot * directDotWeighting) + - (horizDot * horizDotWeighting); + (horizDot * horizDotWeighting * 0.5f); } else { @@ -169,7 +183,7 @@ namespace SupaLidlGame.Characters } else { - float dot = _weightDirs[i].Dot(_weightDirs[j]); + float dot = _weightDirs[i].Dot(_weightDirs[j]) / 2; _weights[j] -= (dot + 1) / 2; } } diff --git a/Characters/Player.cs b/Characters/Player.cs index 457ee19..9b94b8c 100644 --- a/Characters/Player.cs +++ b/Characters/Player.cs @@ -1,13 +1,63 @@ using Godot; -using System; +using SupaLidlGame.Items; namespace SupaLidlGame.Characters { public partial class Player : Character { + private AnimatedSprite2D _sprite; + private string _spriteAnim; + + public string Animation + { + get => _sprite.Animation; + set + { + // the Player.Animation property might be set before this node is + // even ready, so if _sprite is null, then we just hold the new + // animation in a temp value, which will be assigned once this + // node is ready + if (_sprite is null) + { + _spriteAnim = value; + return; + } + + _sprite.Animation = value; + } + } + public override void _Ready() { + _sprite = GetNode("Sprite"); + if (_spriteAnim != default) + { + _sprite.Animation = _spriteAnim; + } + } + public override void ModifyVelocity() + { + if (StateMachine.State is State.PlayerRollState) + { + Velocity *= 2; + } + + if (Inventory.SelectedItem is Weapon weapon) + { + /*if (weapon is Sword sword) + { + if (sword.IsAttacking) + { + Velocity *= 0.5f; + } + } + else*/ + if (weapon.IsUsing) + { + Velocity *= 0.75f; + } + } } } } diff --git a/Characters/Player.tscn b/Characters/Player.tscn index 87373b7..789082e 100644 --- a/Characters/Player.tscn +++ b/Characters/Player.tscn @@ -1,28 +1,81 @@ -[gd_scene load_steps=11 format=3 uid="uid://bncaar8vp3b84"] +[gd_scene load_steps=22 format=3 uid="uid://bncaar8vp3b84"] [ext_resource type="Script" path="res://Characters/Player.cs" id="1_flygr"] -[ext_resource type="Texture2D" uid="uid://bw052v8ikfget" path="res://icon.svg" id="2_xmgd1"] +[ext_resource type="Texture2D" uid="uid://dxymfduyrbuvx" path="res://Sprites/Characters/forsen.png" id="2_swjho"] [ext_resource type="Script" path="res://Characters/States/Machine.cs" id="3_npkjp"] [ext_resource type="Script" path="res://Characters/States/PlayerIdleState.cs" id="4_4k4mb"] [ext_resource type="Script" path="res://Characters/States/PlayerMoveState.cs" id="5_tx5rw"] [ext_resource type="Script" path="res://Characters/States/PlayerRollState.cs" id="6_6bgrj"] +[ext_resource type="Script" path="res://Characters/States/PlayerAttackState.cs" id="7_4cuhw"] [ext_resource type="PackedScene" uid="uid://cajlwb67xenfy" path="res://Items/Weapons/Sword.tscn" id="7_4rxuv"] [ext_resource type="Script" path="res://Items/Inventory.cs" id="7_xyenu"] +[ext_resource type="PackedScene" uid="uid://cjgxyhgcyvsv7" path="res://BoundingBoxes/Hurtbox.tscn" id="9_avyu4"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_us1ce"] +atlas = ExtResource("2_swjho") +region = Rect2(0, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_hn4kf"] +atlas = ExtResource("2_swjho") +region = Rect2(24, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_wfbeq"] +atlas = ExtResource("2_swjho") +region = Rect2(48, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_qlmwk"] +atlas = ExtResource("2_swjho") +region = Rect2(72, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_l1vgu"] +atlas = ExtResource("2_swjho") +region = Rect2(96, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_ytlaa"] +atlas = ExtResource("2_swjho") +region = Rect2(120, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_1q30d"] +atlas = ExtResource("2_swjho") +region = Rect2(144, 0, 24, 24) + +[sub_resource type="SpriteFrames" id="SpriteFrames_2h7cf"] +animations = [{ +"frames": [SubResource("AtlasTexture_us1ce"), SubResource("AtlasTexture_hn4kf")], +"loop": true, +"name": &"idle", +"speed": 5.0 +}, { +"frames": [SubResource("AtlasTexture_wfbeq"), SubResource("AtlasTexture_qlmwk"), SubResource("AtlasTexture_l1vgu"), SubResource("AtlasTexture_ytlaa"), SubResource("AtlasTexture_1q30d")], +"loop": true, +"name": &"move", +"speed": 12.0 +}] [sub_resource type="RectangleShape2D" id="RectangleShape2D_bfqew"] -size = Vector2(32, 16) +size = Vector2(16, 8) [sub_resource type="LabelSettings" id="LabelSettings_q5h1n"] font_size = 24 -[node name="Player" type="CharacterBody2D" node_paths=PackedStringArray("StateMachine")] +[sub_resource type="RectangleShape2D" id="RectangleShape2D_cjk6b"] +size = Vector2(16, 24) + +[node name="Player" type="CharacterBody2D" node_paths=PackedStringArray("Sprite", "Inventory", "StateMachine")] +texture_filter = 3 +y_sort_enabled = true script = ExtResource("1_flygr") Speed = 64.0 +Mass = 1.0 +Sprite = NodePath("Sprite") +Inventory = NodePath("Inventory") StateMachine = NodePath("StateMachine") +Faction = 1 -[node name="Sprite2D" type="Sprite2D" parent="."] -scale = Vector2(0.25, 0.25) -texture = ExtResource("2_xmgd1") +[node name="Sprite" type="AnimatedSprite2D" parent="."] +frames = SubResource("SpriteFrames_2h7cf") +animation = &"move" +playing = true [node name="CollisionShape2D" type="CollisionShape2D" parent="."] position = Vector2(0, 8) @@ -38,19 +91,24 @@ InitialState = NodePath("Idle") Character = NodePath("..") DebugLevel = 2 -[node name="Idle" type="Node" parent="StateMachine" node_paths=PackedStringArray("MoveState")] +[node name="Idle" type="Node" parent="StateMachine" node_paths=PackedStringArray("MoveState", "IdleState")] script = ExtResource("4_4k4mb") MoveState = NodePath("../Move") +IdleState = NodePath("") -[node name="Move" type="Node" parent="StateMachine" node_paths=PackedStringArray("IdleState", "RollState")] +[node name="Move" type="Node" parent="StateMachine" node_paths=PackedStringArray("RollState", "IdleState")] script = ExtResource("5_tx5rw") -IdleState = NodePath("../Idle") RollState = NodePath("../Roll") +IdleState = NodePath("../Idle") [node name="Roll" type="Node" parent="StateMachine" node_paths=PackedStringArray("IdleState")] script = ExtResource("6_6bgrj") IdleState = NodePath("../Idle") +[node name="Attack" type="Node" parent="StateMachine" node_paths=PackedStringArray("IdleState")] +script = ExtResource("7_4cuhw") +IdleState = NodePath("../Idle") + [node name="Debug" type="Control" parent="."] layout_mode = 3 anchors_preset = 0 @@ -67,7 +125,17 @@ horizontal_alignment = 1 [node name="Node" type="Node" parent="."] [node name="Inventory" type="Node2D" parent="."] +y_sort_enabled = true script = ExtResource("7_xyenu") [node name="Sword" parent="Inventory" instance=ExtResource("7_4rxuv")] -position = Vector2(21, 0) + +[node name="Hurtbox" parent="." instance=ExtResource("9_avyu4")] +Faction = 1 + +[node name="CollisionShape2D" parent="Hurtbox" index="0"] +shape = SubResource("RectangleShape2D_cjk6b") + +[connection signal="ReceivedDamage" from="Hurtbox" to="." method="_on_hurtbox_received_damage"] + +[editable path="Hurtbox"] diff --git a/Characters/States/CharacterState.cs b/Characters/States/CharacterState.cs index 2d64e41..185a6d0 100644 --- a/Characters/States/CharacterState.cs +++ b/Characters/States/CharacterState.cs @@ -27,6 +27,7 @@ namespace SupaLidlGame.Characters.State public virtual CharacterState PhysicsProcess(double delta) { Character.Velocity = Character.Direction * Character.Speed; + Character.ModifyVelocity(); Character.MoveAndSlide(); return null; } diff --git a/Characters/States/Machine.cs b/Characters/States/Machine.cs index bba2291..07f284e 100644 --- a/Characters/States/Machine.cs +++ b/Characters/States/Machine.cs @@ -24,6 +24,9 @@ namespace SupaLidlGame.Characters.State public void ChangeState(CharacterState nextState, bool isProxied = false) { + if (nextState is null) + return; + if (DebugLevel >= 2) { if (State is not null) diff --git a/Characters/States/NPCIdleState.cs b/Characters/States/NPCIdleState.cs index 45085d5..3fd4b95 100644 --- a/Characters/States/NPCIdleState.cs +++ b/Characters/States/NPCIdleState.cs @@ -16,5 +16,11 @@ namespace SupaLidlGame.Characters.State } return null; } + + public override CharacterState Enter(CharacterState previousState) + { + Character.Sprite.Play("idle"); + return base.Enter(previousState); + } } } diff --git a/Characters/States/NPCMoveState.cs b/Characters/States/NPCMoveState.cs index 4e250b0..4350009 100644 --- a/Characters/States/NPCMoveState.cs +++ b/Characters/States/NPCMoveState.cs @@ -16,5 +16,11 @@ namespace SupaLidlGame.Characters.State } return null; } + + public override CharacterState Enter(CharacterState previousState) + { + Character.Sprite.Play("move"); + return base.Enter(previousState); + } } } diff --git a/Characters/States/PlayerAttackState.cs b/Characters/States/PlayerAttackState.cs index 8bf1a59..730eafc 100644 --- a/Characters/States/PlayerAttackState.cs +++ b/Characters/States/PlayerAttackState.cs @@ -1,25 +1,38 @@ using Godot; -using Godot.Collections; -using Godot.NativeInterop; +using SupaLidlGame.Items; namespace SupaLidlGame.Characters.State { public partial class PlayerAttackState : PlayerState { - [Export] - public CharacterState IdleState { get; set; } - - private float _attackTime = 0; + private double _attackTime = 0; public override CharacterState Enter(CharacterState previousState) { - _attackTime = 0; + if (Character.Inventory.SelectedItem is Weapon weapon) + { + _attackTime = weapon.UseTime; + weapon.Visible = true; + Character.Inventory.SelectedItem.Use(); + } + else + { + return IdleState; + } return base.Enter(previousState); } public override void Exit(CharacterState nextState) { - _attackTime = 0; + if (Character.Inventory.SelectedItem is null) + { + + } + Character.Inventory.SelectedItem.Deuse(); + if (Character.Inventory.SelectedItem is Weapon weapon) + { + //weapon.Visible = false; + } base.Exit(nextState); } @@ -30,11 +43,16 @@ namespace SupaLidlGame.Characters.State public override CharacterState PhysicsProcess(double delta) { + Character.Velocity *= 0.5f; return base.PhysicsProcess(delta); } public override CharacterState Process(double delta) { + if ((_attackTime -= delta) <= 0) + { + return IdleState; + } return base.Process(delta); } } diff --git a/Characters/States/PlayerIdleState.cs b/Characters/States/PlayerIdleState.cs index 080ad51..aa84471 100644 --- a/Characters/States/PlayerIdleState.cs +++ b/Characters/States/PlayerIdleState.cs @@ -12,7 +12,7 @@ namespace SupaLidlGame.Characters.State GD.Print("Entered idle state"); if (previousState is not PlayerMoveState) { - if (Character.Direction.LengthSquared() > 0) + if (Character.Direction.LengthSquared() > 0.01f) { // other states like attacking or rolling can just delegate // the work of checking if the player is moving to this idle @@ -21,6 +21,7 @@ namespace SupaLidlGame.Characters.State return MoveState; } } + _player.Animation = "idle"; return base.Enter(previousState); } diff --git a/Characters/States/PlayerMoveState.cs b/Characters/States/PlayerMoveState.cs index 440824a..9f27a20 100644 --- a/Characters/States/PlayerMoveState.cs +++ b/Characters/States/PlayerMoveState.cs @@ -1,18 +1,17 @@ using Godot; +using SupaLidlGame.Items; namespace SupaLidlGame.Characters.State { public partial class PlayerMoveState : PlayerState { [Export] - public CharacterState IdleState { get; set; } - - [Export] - public CharacterState RollState { get; set; } + public PlayerRollState RollState { get; set; } public override CharacterState Enter(CharacterState previousState) { Godot.GD.Print("Started moving"); + _player.Animation = "move"; return base.Enter(previousState); } @@ -30,9 +29,19 @@ namespace SupaLidlGame.Characters.State { if (@event.IsActionPressed("roll")) { - return RollState; + if (Character.Inventory.SelectedItem is Weapon weapon) + { + if (!weapon.IsUsing) + { + return RollState; + } + } + else + { + return RollState; + } } - return null; + return base.Input(@event); } } } diff --git a/Characters/States/PlayerRollState.cs b/Characters/States/PlayerRollState.cs index 808d8aa..14ee519 100644 --- a/Characters/States/PlayerRollState.cs +++ b/Characters/States/PlayerRollState.cs @@ -4,9 +4,6 @@ namespace SupaLidlGame.Characters.State { public partial class PlayerRollState : PlayerState { - [Export] - public CharacterState IdleState { get; set; } - private double _timeLeftToRoll = 0; private Vector2 _rollDirection = Vector2.Zero; @@ -16,6 +13,7 @@ namespace SupaLidlGame.Characters.State _timeLeftToRoll = 0.5; // roll the direction we were previously moving in _rollDirection = Character.Direction; + Character.Target = Character.Direction; return base.Enter(previousState); } @@ -38,11 +36,13 @@ namespace SupaLidlGame.Characters.State return null; } + /* public override CharacterState PhysicsProcess(double delta) { Character.Velocity = Character.Direction * Character.Speed * 1.5f; Character.MoveAndSlide(); return null; } + */ } } diff --git a/Characters/States/PlayerState.cs b/Characters/States/PlayerState.cs index c802eea..b15283f 100644 --- a/Characters/States/PlayerState.cs +++ b/Characters/States/PlayerState.cs @@ -1,3 +1,6 @@ +using Godot; +using SupaLidlGame.Items; + namespace SupaLidlGame.Characters.State { public partial class PlayerState : CharacterState @@ -5,10 +8,59 @@ namespace SupaLidlGame.Characters.State //public PlayerMachine PlayerMachine => Machine as PlayerMachine; protected Player _player => Character as Player; + [Export] + public PlayerIdleState IdleState { get; set; } + + public override CharacterState Input(InputEvent @event) + { + /* + if (@event is InputEventKey inputEventKey) + { + GD.Print("hello"); + if (inputEventKey.Keycode == Key.G) + { + GD.Print("hi"); + } + }*/ + if (@event.IsActionPressed("equip")) + { + Character.Inventory.SelectedItem = Character.Inventory.GetNode("Sword"); + //Character.Inventory.AddItem(); + } + + if (@event.IsActionPressed("attack1")) + { + if (Character.Inventory.SelectedItem is not null) + { + Character.Inventory.SelectedItem.Use(); + //return AttackState; + } + } + + //if (this is PlayerAttackState) + //{ + // if (@event.IsActionReleased("attack1")) + // { + // return IdleState; + // } + //} + + return base.Input(@event); + } + public override CharacterState Process(double delta) { Character.Direction = Godot.Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down"); + Vector2 mousePos = Character.GetGlobalMousePosition(); + Vector2 dirToMouse = Character.GlobalPosition.DirectionTo(mousePos); + if (Character.Inventory.SelectedItem is Weapon weapon) + { + if (!weapon.IsUsing) + { + Character.Target = dirToMouse; + } + } return base.Process(delta); } } diff --git a/Items/Inventory.cs b/Items/Inventory.cs index 0a1346d..bee0019 100644 --- a/Items/Inventory.cs +++ b/Items/Inventory.cs @@ -32,7 +32,11 @@ namespace SupaLidlGame.Items _selectedItem = value; - _selectedItem.Equip(Character); + // this is to handle if item was manually unequipped + if (_selectedItem is not null) + { + _selectedItem.Equip(Character); + } } } @@ -43,23 +47,27 @@ namespace SupaLidlGame.Items return null; } + item.CharacterOwner = Character; + item.Visible = false; Items.Add(item); return item; } public Item DropItem(Item item) { + item.CharacterOwner = null; + item.Visible = true; throw new System.NotImplementedException(); } public override void _Ready() { - Owner = GetParent(); + Character = GetParent(); foreach (Node child in GetChildren()) { if (child is Item item) { - Items.Add(item); + AddItem(item); } } base._Ready(); diff --git a/Items/Item.cs b/Items/Item.cs index af78ec6..c1813b5 100644 --- a/Items/Item.cs +++ b/Items/Item.cs @@ -8,6 +8,8 @@ namespace SupaLidlGame.Items [Export] public string Description { get; set; } + public Character CharacterOwner { get; set; } + public abstract void Equip(Character character); public abstract void Unequip(Character character); diff --git a/Items/Weapon.cs b/Items/Weapon.cs index 62815ae..1c6619a 100644 --- a/Items/Weapon.cs +++ b/Items/Weapon.cs @@ -7,7 +7,7 @@ namespace SupaLidlGame.Items { public double RemainingUseTime { get; protected set; } = 0; - public bool CanStartAttack => RemainingUseTime <= 0; + public bool IsUsing => RemainingUseTime > 0; /// /// How much damage in HP that this weapon deals. @@ -46,9 +46,25 @@ namespace SupaLidlGame.Items Character = null; } + public override void Use() + { + RemainingUseTime = UseTime; + } + public override void Deuse() { } + + public override void _Process(double delta) + { + if (RemainingUseTime > 0) + { + if ((RemainingUseTime -= delta) <= 0) + { + Deuse(); + } + } + } } } diff --git a/Items/Weapons/Sword.cs b/Items/Weapons/Sword.cs index 9fe20d3..b6c115f 100644 --- a/Items/Weapons/Sword.cs +++ b/Items/Weapons/Sword.cs @@ -6,22 +6,81 @@ namespace SupaLidlGame.Items.Weapons { public partial class Sword : Weapon { + public double RemainingAttackTime { get; protected set; } = 0; + + public bool IsAttacking => RemainingAttackTime > 0; + [Export] public Hitbox Hitbox { get; set; } + [Export] + public AnimationPlayer AnimationPlayer { get; set; } + + /// + /// The time frame in seconds for which the weapon will deal damage. + /// + /// + /// The value of AttackTime should be less than the + /// value of UseTime + /// + [Export] + public double AttackTime { get; set; } = 0; + public override void Equip(Character character) { + Visible = true; base.Equip(character); + Hitbox.Faction = character.Faction; // character is null before base + } + + public override void Unequip(Character character) + { + Visible = false; + base.Unequip(character); } public override void Use() { - Hitbox.IsEnabled = true; + if (RemainingUseTime > 0) + { + return; + } + + RemainingAttackTime = AttackTime; + + AnimationPlayer.Play("use"); + Hitbox.IsDisabled = false; + base.Use(); } public override void Deuse() { - Hitbox.IsEnabled = false; + AnimationPlayer.Stop(); + Deattack(); + base.Deuse(); + } + + protected void Deattack() + { + Hitbox.IsDisabled = true; + Hitbox.ResetIgnoreList(); + } + + public override void _Ready() + { + Hitbox.Damage = Damage; + } + + public override void _Process(double delta) + { + if (RemainingAttackTime > 0) + { + if ((RemainingAttackTime -= delta) <= 0) + { + Deattack(); + } + } + base._Process(delta); } } } diff --git a/Items/Weapons/Sword.tscn b/Items/Weapons/Sword.tscn index 73e6f9f..f6496dc 100644 --- a/Items/Weapons/Sword.tscn +++ b/Items/Weapons/Sword.tscn @@ -1,26 +1,159 @@ -[gd_scene load_steps=5 format=3 uid="uid://cajlwb67xenfy"] +[gd_scene load_steps=13 format=3 uid="uid://cajlwb67xenfy"] [ext_resource type="Script" path="res://Items/Weapons/Sword.cs" id="1_mlo73"] [ext_resource type="Texture2D" uid="uid://dt6u8p4h6g7le" path="res://Sprites/knife.png" id="2_dmsp2"] [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"] [sub_resource type="CapsuleShape2D" id="CapsuleShape2D_yln58"] radius = 8.0 -height = 24.0 +height = 32.0 -[node name="Sword" type="Node2D" node_paths=PackedStringArray("Hitbox")] +[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 + +[sub_resource type="Gradient" id="Gradient_2ablm"] +offsets = PackedFloat32Array(0.835938, 0.992188) +colors = PackedColorArray(1, 1, 1, 0.498039, 1, 1, 1, 0) + +[sub_resource type="Animation" id="Animation_b7327"] +length = 0.001 +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": 0, +"values": [-0.785398] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("Anchor/Sprite2D:rotation") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [-1.19209e-07] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("Anchor/Sprite2D:position") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Vector2(8, 1)] +} + +[sub_resource type="Animation" id="Animation_mv7y2"] +resource_name = "idle" + +[sub_resource type="Animation" id="Animation_orc8t"] +resource_name = "use" +length = 0.5 +loop_mode = 1 +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 = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.1, 0.3, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1), +"update": 0, +"values": [-0.785398, 1.5708, 1.5708, -0.785398] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("Anchor/Sprite2D:rotation") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0, 0.1, 0.3, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1), +"update": 0, +"values": [-1.19209e-07, 0.785398, 0.785398, -1.19209e-07] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("Anchor/Sprite2D:position") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0, 0.1, 0.3, 0.5), +"transitions": PackedFloat32Array(1, 1, 1, 1), +"update": 0, +"values": [Vector2(8, 1), Vector2(12, -1), Vector2(12, -1), Vector2(8, 1)] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_tao4k"] +_data = { +"RESET": SubResource("Animation_b7327"), +"idle": SubResource("Animation_mv7y2"), +"use": SubResource("Animation_orc8t") +} + +[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_1lid1"] + +[node name="Sword" type="Node2D" node_paths=PackedStringArray("Hitbox", "AnimationPlayer")] texture_filter = 3 +y_sort_enabled = true script = ExtResource("1_mlo73") -Hitbox = NodePath("Hitbox") +Hitbox = NodePath("Anchor/Sprite2D/Hitbox") +AnimationPlayer = NodePath("AnimationPlayer") +AttackTime = 0.1 +Damage = 20.0 +UseTime = 0.5 +Description = "A basic sword." -[node name="Sprite2D" type="Sprite2D" parent="."] +[node name="Anchor" type="Node2D" parent="."] +rotation = -0.785398 +y_sort_enabled = true + +[node name="Sprite2D" type="Sprite2D" parent="Anchor"] +position = Vector2(8, 1) +y_sort_enabled = true texture = ExtResource("2_dmsp2") -[node name="Hitbox" parent="." instance=ExtResource("3_up3ob")] +[node name="Hitbox" parent="Anchor/Sprite2D" instance=ExtResource("3_up3ob")] +IsDisabled = true -[node name="CollisionShape2D" parent="Hitbox" index="0"] +[node name="CollisionShape2D" parent="Anchor/Sprite2D/Hitbox" index="0"] +position = Vector2(0, -4) shape = SubResource("CapsuleShape2D_yln58") +disabled = true + +[node name="Trail" parent="Anchor" node_paths=PackedStringArray("Tracking") instance=ExtResource("4_pt6lq")] +position = Vector2(2.40734, -0.55655) +rotation = 0.945464 +scale = Vector2(1, 1) +width_curve = SubResource("Curve_4cxtp") +gradient = SubResource("Gradient_2ablm") +Tracking = NodePath("../Sprite2D") [node name="AnimationPlayer" type="AnimationPlayer" parent="."] +libraries = { +"": SubResource("AnimationLibrary_tao4k") +} -[editable path="Hitbox"] +[node name="AnimationTree" type="AnimationTree" parent="."] +tree_root = SubResource("AnimationNodeAnimation_1lid1") +anim_player = NodePath("../AnimationPlayer") +active = true + +[editable path="Anchor/Sprite2D/Hitbox"] diff --git a/README.org b/README.org new file mode 100644 index 0000000..19a9833 --- /dev/null +++ b/README.org @@ -0,0 +1,7 @@ +#+TITLE: SupaLidlGame + +Forsen-related game + +*NOTE:* although the latest version at the time of writing is ~4.0.beta5.mono~, this project only works under ~4.0.beta4.mono~. The latest beta removes node exports, which was previously a feature in Beta 4. + +[[https://github.com/godotengine/godot/pull/67055]] diff --git a/Scenes/Level.tscn b/Scenes/Level.tscn index 1ef7d43..2603503 100644 --- a/Scenes/Level.tscn +++ b/Scenes/Level.tscn @@ -1109,7 +1109,7 @@ tile_set = SubResource("TileSet_18c7j") format = 2 layer_0/name = "Walls" layer_0/y_sort_enabled = true -layer_0/tile_data = PackedInt32Array(-131067, 458752, 0, 131065, 458752, 0) +layer_0/tile_data = PackedInt32Array(131065, 458752, 0, -65531, 458752, 0) layer_1/name = "Ground 2" layer_1/enabled = true layer_1/modulate = Color(1, 1, 1, 1) @@ -1134,17 +1134,16 @@ layer_3/tile_data = PackedInt32Array(-458765, 393216, 1, -393229, 327680, 0, -39 [node name="Player" parent="TileMap" instance=ExtResource("1_m35hr")] position = Vector2(-81, -34) -scale = Vector2(0.25, 0.25) motion_mode = 1 [node name="ExampleEnemy" parent="TileMap" instance=ExtResource("2_uti3y")] -position = Vector2(-4, 60) -scale = Vector2(0.25, 0.25) +position = Vector2(38, 42) +scale = Vector2(1.00571, 1) [node name="ExampleEnemy2" parent="TileMap" instance=ExtResource("2_uti3y")] -position = Vector2(-17, -4) -scale = Vector2(0.25, 0.25) +position = Vector2(156, 58) +scale = Vector2(1.00571, 1) [node name="ExampleEnemy3" parent="TileMap" instance=ExtResource("2_uti3y")] -position = Vector2(34, 25) -scale = Vector2(0.25, 0.25) +position = Vector2(175, -15) +scale = Vector2(1.00571, 1) diff --git a/Sprites/Characters/forsen.ase b/Sprites/Characters/forsen.ase new file mode 100644 index 0000000000000000000000000000000000000000..042eb6621c47ebb2bd24e0bcfcfc4d51dc701435 GIT binary patch literal 10833 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 mDcqrvClfn?KShe2jQ;`;d@}goxdz1m0000 + /// The faction index that this entity belongs to. + /// + [Export] + public ushort Faction { get; set; } + } +} diff --git a/Utils/Trail.cs b/Utils/Trail.cs new file mode 100644 index 0000000..56c9825 --- /dev/null +++ b/Utils/Trail.cs @@ -0,0 +1,21 @@ +using Godot; + +public partial class Trail : Line2D +{ + [Export] + public int MaximumPoints { get; set; } + + [Export] + public Node2D Tracking { get; set; } + + public override void _Process(double delta) + { + Vector2 point = Tracking.Position; + AddPoint(point); + + while (Points.Length > MaximumPoints) + { + RemovePoint(0); + } + } +} diff --git a/Utils/Trail.tscn b/Utils/Trail.tscn new file mode 100644 index 0000000..69af05e --- /dev/null +++ b/Utils/Trail.tscn @@ -0,0 +1,8 @@ +[gd_scene load_steps=2 format=3 uid="uid://cojxmcin13ihm"] + +[ext_resource type="Script" path="res://Utils/Trail.cs" id="1_t42kk"] + +[node name="Trail" type="Line2D"] +show_behind_parent = true +script = ExtResource("1_t42kk") +MaximumPoints = 16 diff --git a/project.godot b/project.godot index c9241d8..bafb510 100644 --- a/project.godot +++ b/project.godot @@ -51,6 +51,16 @@ roll={ , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"unicode":0,"echo":false,"script":null) ] } +attack1={ +"deadzone": 0.5, +"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"pressed":false,"double_click":false,"script":null) +] +} +equip={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"unicode":0,"echo":false,"script":null) +] +} [physics]