From 706354e813590cacd8c47e71a5478d001a31846f Mon Sep 17 00:00:00 2001 From: HumanoidSandvichDispenser Date: Sat, 3 Jun 2023 18:21:46 -0700 Subject: [PATCH] refactor file scoped namespace --- BoundingBoxes/BoundingBox.cs | 11 +- BoundingBoxes/ConnectorBox.cs | 111 +++--- BoundingBoxes/Hitbox.cs | 141 ++++---- BoundingBoxes/Hurtbox.cs | 59 ++-- Characters/Character.cs | 411 +++++++++++------------ Characters/Enemy.cs | 25 +- Characters/NPC.cs | 355 ++++++++++---------- Characters/Player.cs | 105 +++--- Characters/Thinkers/Thinker.cs | 13 +- Entities/Campfire.cs | 31 +- Entities/Projectile.cs | 75 ++--- Extensions/AudioStreamPlayer2D.cs | 119 ++++--- Extensions/Node.cs | 57 ++-- Extensions/Node2DExtensions.cs | 13 +- Extensions/Vector2.cs | 59 ++-- Items/Inventory.cs | 207 ++++++------ Items/Item.cs | 81 +++-- Items/Weapon.cs | 195 ++++++----- Items/Weapons/IParryable.cs | 15 +- Items/Weapons/Railgun.cs | 29 +- Items/Weapons/Ranged.cs | 73 ++-- Items/Weapons/Sword.cs | 389 +++++++++++---------- Scenes/Level.tscn | 3 +- Scenes/Map.cs | 73 ++-- State/Character/CharacterState.cs | 125 ++++--- State/Character/CharacterStateMachine.cs | 51 ++- State/Character/NPCIdleState.cs | 33 +- State/Character/NPCMoveState.cs | 33 +- State/Character/NPCState.cs | 19 +- State/Character/PlayerIdleState.cs | 53 ++- State/Character/PlayerMoveState.cs | 57 ++-- State/Character/PlayerRollState.cs | 57 ++-- State/Character/PlayerState.cs | 101 +++--- State/IState.cs | 37 +- State/StateMachine.cs | 57 ++-- State/Weapon/RangedFireState.cs | 53 ++- State/Weapon/RangedIdleState.cs | 41 ++- State/Weapon/SwordAnticipateState.cs | 67 ++-- State/Weapon/SwordAttackState.cs | 141 ++++---- State/Weapon/SwordIdleState.cs | 71 ++-- State/Weapon/WeaponState.cs | 25 +- State/Weapon/WeaponStateMachine.cs | 45 ++- Tests/ContextBasedSteering.cs | 81 +++-- UI/FloatingText.cs | 77 +++-- Utils/IFaction.cs | 17 +- Utils/PlayerCamera.cs | 71 ++-- Utils/Spawner.cs | 81 +++-- Utils/World.cs | 241 +++++++------ 48 files changed, 2118 insertions(+), 2166 deletions(-) diff --git a/BoundingBoxes/BoundingBox.cs b/BoundingBoxes/BoundingBox.cs index cbd20cb..f1972ef 100644 --- a/BoundingBoxes/BoundingBox.cs +++ b/BoundingBoxes/BoundingBox.cs @@ -1,11 +1,10 @@ using Godot; using SupaLidlGame.Utils; -namespace SupaLidlGame.BoundingBoxes +namespace SupaLidlGame.BoundingBoxes; + +public abstract partial class BoundingBox : Area2D, IFaction { - public abstract partial class BoundingBox : Area2D, IFaction - { - [Export] - public ushort Faction { get; set; } - } + [Export] + public ushort Faction { get; set; } } diff --git a/BoundingBoxes/ConnectorBox.cs b/BoundingBoxes/ConnectorBox.cs index c855f62..e853b20 100644 --- a/BoundingBoxes/ConnectorBox.cs +++ b/BoundingBoxes/ConnectorBox.cs @@ -2,71 +2,70 @@ using Godot; using System; using SupaLidlGame.Characters; -namespace SupaLidlGame.BoundingBoxes +namespace SupaLidlGame.BoundingBoxes; + +public partial class ConnectorBox : Area2D { - public partial class ConnectorBox : Area2D + [Signal] + public delegate void RequestedEnterEventHandler( + ConnectorBox box, + Player player); + + [Export] + public string ToArea { get; set; } + + [Export] + public string ToConnector { get; set; } + + [Export] + public string Identifier { get; set; } + + /// + /// Determines if the connector requires the user to interact to enter + /// the connector + /// + [Export] + public bool RequiresInteraction { get; set; } = false; + + [Export] + public CollisionShape2D Collision { get; set; } + + private Player _player = null; + + public override void _Ready() { - [Signal] - public delegate void RequestedEnterEventHandler( - ConnectorBox box, - Player player); - - [Export] - public string ToArea { get; set; } - - [Export] - public string ToConnector { get; set; } - - [Export] - public string Identifier { get; set; } - - /// - /// Determines if the connector requires the user to interact to enter - /// the connector - /// - [Export] - public bool RequiresInteraction { get; set; } = false; - - [Export] - public CollisionShape2D Collision { get; set; } - - private Player _player = null; - - public override void _Ready() + if (Collision is null) { - if (Collision is null) - { - throw new NullReferenceException("Collision not specified"); - } - - BodyEntered += (Node2D body) => - { - if (body is Player player) - { - _player = player; - } - }; - - BodyExited += (Node2D body) => - { - if (body is Player) - { - _player = null; - } - }; + throw new NullReferenceException("Collision not specified"); } - public override void _Process(double delta) + BodyEntered += (Node2D body) => { - if (Input.IsActionJustReleased("interact")) + if (body is Player player) { - if (_player is not null) - { - EmitSignal(SignalName.RequestedEnter, this, _player); - } + _player = player; } + }; - base._Process(delta); + BodyExited += (Node2D body) => + { + if (body is Player) + { + _player = null; + } + }; + } + + public override void _Process(double delta) + { + if (Input.IsActionJustReleased("interact")) + { + if (_player is not null) + { + EmitSignal(SignalName.RequestedEnter, this, _player); + } } + + base._Process(delta); } } diff --git a/BoundingBoxes/Hitbox.cs b/BoundingBoxes/Hitbox.cs index b7950ae..bbe1365 100644 --- a/BoundingBoxes/Hitbox.cs +++ b/BoundingBoxes/Hitbox.cs @@ -3,103 +3,102 @@ using Godot; using SupaLidlGame.Characters; using SupaLidlGame.Items; -namespace SupaLidlGame.BoundingBoxes +namespace SupaLidlGame.BoundingBoxes; + +public partial class Hitbox : BoundingBox { - public partial class Hitbox : BoundingBox + private HashSet _ignoreList = new HashSet(); + + [Signal] + public delegate void HitEventHandler(BoundingBox box); + + [Export] + public float Damage { get; set; } + + private bool _isDisabled = false; + + /// + /// Getter/setter for the CollisionShape2D's Disabled property. + /// + [Export] + public bool IsDisabled { - private HashSet _ignoreList = new HashSet(); - - [Signal] - public delegate void HitEventHandler(BoundingBox box); - - [Export] - public float Damage { get; set; } - - private bool _isDisabled = false; - - /// - /// Getter/setter for the CollisionShape2D's Disabled property. - /// - [Export] - public bool IsDisabled + get => _collisionShape.Disabled; + set { - get => _collisionShape.Disabled; - set + _isDisabled = value; + if (_collisionShape is not null) { - _isDisabled = value; - if (_collisionShape is not null) + _collisionShape.Disabled = value; + if (value) { - _collisionShape.Disabled = value; - if (value) - { - DamageStartTime = Time.GetTicksMsec(); - } + DamageStartTime = Time.GetTicksMsec(); } } } + } - [Export] - public float Knockback { get; set; } + [Export] + public float Knockback { get; set; } - public Character Inflictor { get; set; } + public Character Inflictor { get; set; } - public ulong DamageStartTime { get; set; } + public ulong DamageStartTime { get; set; } - private CollisionShape2D _collisionShape; + private CollisionShape2D _collisionShape; - private bool _isParrying = false; + private bool _isParrying = false; - public override void _Ready() + public override void _Ready() + { + _collisionShape = GetNode("CollisionShape2D"); + IsDisabled = _isDisabled; // sets _collisionShape.Disabled + } + + private bool ShouldParry(Hitbox box) + { + Node parent = GetParent(); + + // if this hitbox does not even belong to a weapon, skip + if (parent is not Weapon) { - _collisionShape = GetNode("CollisionShape2D"); - IsDisabled = _isDisabled; // sets _collisionShape.Disabled - } - - private bool ShouldParry(Hitbox box) - { - Node parent = GetParent(); - - // if this hitbox does not even belong to a weapon, skip - if (parent is not Weapon) - { - return false; - } - - var weapon = parent as Weapon; - - // if we hit a hitbox, we can parry if it can be parried - if (box.GetParent() is Weapon other) - { - return weapon.IsParryable && other.IsParryable; - } - return false; } - public void _on_area_entered(Area2D area) + var weapon = parent as Weapon; + + // if we hit a hitbox, we can parry if it can be parried + if (box.GetParent() is Weapon other) { - if (area is BoundingBox box) + return weapon.IsParryable && other.IsParryable; + } + + return false; + } + + public void _on_area_entered(Area2D area) + { + if (area is BoundingBox box) + { + GD.Print("hit"); + // we don't want to hurt teammates + if (Faction != box.Faction) { - GD.Print("hit"); - // we don't want to hurt teammates - if (Faction != box.Faction) + if (!_ignoreList.Contains(box)) { - if (!_ignoreList.Contains(box)) - { - _ignoreList.Add(box); - EmitSignal(SignalName.Hit, box); - } + _ignoreList.Add(box); + EmitSignal(SignalName.Hit, box); } } } + } - public void ResetIgnoreList() => _ignoreList.Clear(); + public void ResetIgnoreList() => _ignoreList.Clear(); - public bool HasHit(BoundingBox box) => _ignoreList.Contains(box); + public bool HasHit(BoundingBox box) => _ignoreList.Contains(box); - public HashSet Hits - { - get => _ignoreList; - } + public HashSet Hits + { + get => _ignoreList; } } diff --git a/BoundingBoxes/Hurtbox.cs b/BoundingBoxes/Hurtbox.cs index fc01ec7..ca36c51 100644 --- a/BoundingBoxes/Hurtbox.cs +++ b/BoundingBoxes/Hurtbox.cs @@ -2,39 +2,38 @@ using Godot; using SupaLidlGame.Characters; using SupaLidlGame.Utils; -namespace SupaLidlGame.BoundingBoxes +namespace SupaLidlGame.BoundingBoxes; + +public partial class Hurtbox : BoundingBox, IFaction { - public partial class Hurtbox : BoundingBox, IFaction + [Signal] + public delegate void ReceivedDamageEventHandler( + float damage, + Character inflictor, + float knockback, + Vector2 knockbackOrigin = default, + Vector2 knockbackVector = default); + + public override void _Ready() { - [Signal] - public delegate void ReceivedDamageEventHandler( - float damage, - Character inflictor, - float knockback, - Vector2 knockbackOrigin = default, - Vector2 knockbackVector = default); - - public override void _Ready() + if (GetParent() is IFaction factionEntity) { - if (GetParent() is IFaction factionEntity) - { - Faction = factionEntity.Faction; - } - } - - public void InflictDamage( - float damage, - Character inflictor, - float knockback, - Vector2 knockbackOrigin = default, - Vector2 knockbackVector = default) - { - EmitSignal( - SignalName.ReceivedDamage, - damage, - inflictor, - knockback, - knockbackOrigin, knockbackVector); + Faction = factionEntity.Faction; } } + + public void InflictDamage( + float damage, + Character inflictor, + float knockback, + Vector2 knockbackOrigin = default, + Vector2 knockbackVector = default) + { + EmitSignal( + SignalName.ReceivedDamage, + damage, + inflictor, + knockback, + knockbackOrigin, knockbackVector); + } } diff --git a/Characters/Character.cs b/Characters/Character.cs index a21c4b8..74fdbe6 100644 --- a/Characters/Character.cs +++ b/Characters/Character.cs @@ -4,221 +4,220 @@ using SupaLidlGame.Items; using SupaLidlGame.Utils; using SupaLidlGame.State.Character; -namespace SupaLidlGame.Characters +namespace SupaLidlGame.Characters; + +public partial class Character : CharacterBody2D, IFaction { - public partial class Character : CharacterBody2D, IFaction + [Export] + public float Speed { get; protected set; } = 32.0f; + + [Export] + public float Friction { get; protected set; } = 4.0f; + + [Export] + public float Mass { - [Export] - public float Speed { get; protected set; } = 32.0f; - - [Export] - public float Friction { get; protected set; } = 4.0f; - - [Export] - public float Mass + get => _mass; + set { - get => _mass; - set + if (value > 0) + _mass = value; + } + } + + protected float _mass = 1.0f; + + public Vector2 NetImpulse { get; set; } = Vector2.Zero; + + public Vector2 Direction { get; set; } = Vector2.Zero; + + public Vector2 Target { get; set; } = Vector2.Zero; + + [Export] + public float Health + { + get => _health; + set + { + if (!IsAlive && value < 0) { - if (value > 0) - _mass = value; - } - } - - protected float _mass = 1.0f; - - public Vector2 NetImpulse { get; set; } = Vector2.Zero; - - public Vector2 Direction { get; set; } = Vector2.Zero; - - public Vector2 Target { get; set; } = Vector2.Zero; - - [Export] - public float Health - { - get => _health; - set - { - if (!IsAlive && value < 0) - { - return; - } - - _health = value; - if (_health <= 0) - { - Die(); - } - } - } - - public bool IsAlive => Health > 0; - - protected float _health = 100f; - - public double StunTime { get; set; } - - [Export] - public AnimatedSprite2D Sprite { get; set; } - - [Export] - public Inventory Inventory { get; set; } - - [Export] - public CharacterStateMachine 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) - { - if (StateMachine != null) - { - StateMachine.Input(@event); - } - } - - public override void _PhysicsProcess(double delta) - { - if (StateMachine != null) - { - StateMachine.PhysicsProcess(delta); - } - } - - /// - /// Modify the Character's velocity - /// - public virtual void ModifyVelocity() - { - if (StunTime > 0) - { - Velocity *= 0.25f; - } - } - - public virtual void Die() - { - GD.Print("lol died"); - QueueFree(); - } - - public void ApplyImpulse(Vector2 impulse, bool resetVelocity = false) - { - // delta p = F delta t - if (resetVelocity) - Velocity = Vector2.Zero; - NetImpulse += impulse / Mass; - } - - public virtual void Stun(float time) - { - StunTime += time; - } - - 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 UseCurrentItem() - { - if (StunTime > 0) - { - GD.Print("tried to use weapon but stunned"); return; } - if (Inventory.SelectedItem is Weapon weapon) + _health = value; + if (_health <= 0) { - weapon.Use(); - } - } - - public void LookTowardsDirection() - { - if (!Direction.IsZeroApprox()) - { - Target = Direction; - } - } - - public virtual void _on_hurtbox_received_damage( - float damage, - Character inflictor, - float knockback, - Vector2 knockbackOrigin = default, - Vector2 knockbackVector = default) - { - Health -= damage; - - // create damage text - var textScene = GD.Load("res://UI/FloatingText.tscn"); - var instance = textScene.Instantiate(); - instance.Text = Mathf.Round(damage).ToString(); - instance.GlobalPosition = GlobalPosition; - this.GetAncestor().AddChild(instance); - - // apply knockback - Vector2 knockbackDir = knockbackVector; - if (knockbackDir == default) - { - if (knockbackOrigin == default) - { - knockbackOrigin = inflictor.GlobalPosition; - } - - knockbackDir = knockbackOrigin.DirectionTo(GlobalPosition); - } - - ApplyImpulse(knockbackDir.Normalized() * knockback); - - GD.Print("lol"); - - // play damage animation - var anim = GetNode("FlashAnimation"); - if (anim != null) - { - anim.Stop(); - anim.Play("Hurt"); - } - - // if anyone involved is a player, shake their screen - Player plr = inflictor as Player ?? this as Player; - if (plr is not null) - { - plr.Camera.Shake(1, 0.4f); - } - - if (this.GetNode("HurtSound") is AudioStreamPlayer2D sound) - { - // very small pitch deviation - sound.At(GlobalPosition).WithPitchDeviation(0.125f).Play(); + Die(); } } } + + public bool IsAlive => Health > 0; + + protected float _health = 100f; + + public double StunTime { get; set; } + + [Export] + public AnimatedSprite2D Sprite { get; set; } + + [Export] + public Inventory Inventory { get; set; } + + [Export] + public CharacterStateMachine 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) + { + if (StateMachine != null) + { + StateMachine.Input(@event); + } + } + + public override void _PhysicsProcess(double delta) + { + if (StateMachine != null) + { + StateMachine.PhysicsProcess(delta); + } + } + + /// + /// Modify the Character's velocity + /// + public virtual void ModifyVelocity() + { + if (StunTime > 0) + { + Velocity *= 0.25f; + } + } + + public virtual void Die() + { + GD.Print("lol died"); + QueueFree(); + } + + public void ApplyImpulse(Vector2 impulse, bool resetVelocity = false) + { + // delta p = F delta t + if (resetVelocity) + Velocity = Vector2.Zero; + NetImpulse += impulse / Mass; + } + + public virtual void Stun(float time) + { + StunTime += time; + } + + 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 UseCurrentItem() + { + if (StunTime > 0) + { + GD.Print("tried to use weapon but stunned"); + return; + } + + if (Inventory.SelectedItem is Weapon weapon) + { + weapon.Use(); + } + } + + public void LookTowardsDirection() + { + if (!Direction.IsZeroApprox()) + { + Target = Direction; + } + } + + public virtual void _on_hurtbox_received_damage( + float damage, + Character inflictor, + float knockback, + Vector2 knockbackOrigin = default, + Vector2 knockbackVector = default) + { + Health -= damage; + + // create damage text + var textScene = GD.Load("res://UI/FloatingText.tscn"); + var instance = textScene.Instantiate(); + instance.Text = Mathf.Round(damage).ToString(); + instance.GlobalPosition = GlobalPosition; + this.GetAncestor().AddChild(instance); + + // apply knockback + Vector2 knockbackDir = knockbackVector; + if (knockbackDir == default) + { + if (knockbackOrigin == default) + { + knockbackOrigin = inflictor.GlobalPosition; + } + + knockbackDir = knockbackOrigin.DirectionTo(GlobalPosition); + } + + ApplyImpulse(knockbackDir.Normalized() * knockback); + + GD.Print("lol"); + + // play damage animation + var anim = GetNode("FlashAnimation"); + if (anim != null) + { + anim.Stop(); + anim.Play("Hurt"); + } + + // if anyone involved is a player, shake their screen + Player plr = inflictor as Player ?? this as Player; + if (plr is not null) + { + plr.Camera.Shake(1, 0.4f); + } + + if (this.GetNode("HurtSound") is AudioStreamPlayer2D sound) + { + // very small pitch deviation + sound.At(GlobalPosition).WithPitchDeviation(0.125f).Play(); + } + } } diff --git a/Characters/Enemy.cs b/Characters/Enemy.cs index b33b6e1..4d5b335 100644 --- a/Characters/Enemy.cs +++ b/Characters/Enemy.cs @@ -1,19 +1,18 @@ using Godot; using System; -namespace SupaLidlGame.Characters -{ - public partial class Enemy : NPC - { - public override void _Ready() - { - Inventory.SelectedItem = Inventory.GetNode("Sword"); - base._Ready(); - } +namespace SupaLidlGame.Characters; - public override void Die() - { - base.Die(); - } +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/NPC.cs b/Characters/NPC.cs index a834f9d..1e34d27 100644 --- a/Characters/NPC.cs +++ b/Characters/NPC.cs @@ -3,225 +3,224 @@ using SupaLidlGame.Extensions; using SupaLidlGame.Items; using System; -namespace SupaLidlGame.Characters +namespace SupaLidlGame.Characters; + +public partial class NPC : Character { - public partial class NPC : Character + /// + /// Time in seconds it takes for the NPC to think FeelsDankCube + /// + public const float ThinkTime = 0.125f; + + public float[] Weights => _weights; + + protected float _preferredWeightDistance = 64.0f; + protected float _maxWeightDistance = 8.0f; + protected float _preferredWeightDistanceSq = 4096.0f; + protected float _maxWeightDistanceSq = 64.0f; + + [Export] + public float PreferredWeightDistance + { + get => _preferredWeightDistance; + protected set + { + _preferredWeightDistance = value; + _preferredWeightDistanceSq = value * value; + } + } + + [Export] + public float MaxWeightDistance + { + get => _maxWeightDistance; + protected set + { + _maxWeightDistance = value; + _maxWeightDistanceSq = value * value; + } + } + + protected float[] _weights = new float[16]; + protected int _bestWeightIdx; + protected double _thinkTimeElapsed = 0; + protected Vector2 _blockingDir; + protected static readonly Vector2[] _weightDirs = new Vector2[16]; + + static NPC() { - /// - /// Time in seconds it takes for the NPC to think FeelsDankCube - /// - public const float ThinkTime = 0.125f; - - public float[] Weights => _weights; - - protected float _preferredWeightDistance = 64.0f; - protected float _maxWeightDistance = 8.0f; - protected float _preferredWeightDistanceSq = 4096.0f; - protected float _maxWeightDistanceSq = 64.0f; - - [Export] - public float PreferredWeightDistance - { - get => _preferredWeightDistance; - protected set - { - _preferredWeightDistance = value; - _preferredWeightDistanceSq = value * value; - } - } - - [Export] - public float MaxWeightDistance - { - get => _maxWeightDistance; - protected set - { - _maxWeightDistance = value; - _maxWeightDistanceSq = value * value; - } - } - - protected float[] _weights = new float[16]; - protected int _bestWeightIdx; - protected double _thinkTimeElapsed = 0; - protected Vector2 _blockingDir; - protected static readonly Vector2[] _weightDirs = new Vector2[16]; - - static NPC() + for (int i = 0; i < 16; i++) { - for (int i = 0; i < 16; i++) - { - float y = Mathf.Sin(Mathf.Pi * i * 2 / 16); - float x = Mathf.Cos(Mathf.Pi * i * 2 / 16); - _weightDirs[i] = new Vector2(x, y); - } + float y = Mathf.Sin(Mathf.Pi * i * 2 / 16); + float x = Mathf.Cos(Mathf.Pi * i * 2 / 16); + _weightDirs[i] = new Vector2(x, y); } + } - public override void _Ready() - { - base._Ready(); - Array.Fill(_weights, 0); - } + public override void _Ready() + { + base._Ready(); + Array.Fill(_weights, 0); + } - public override void _Draw() - { + public override void _Draw() + { #if DEBUG - for (int i = 0; i < 16; i++) + for (int i = 0; i < 16; i++) + { + Vector2 vec = _weightDirs[i] * _weights[i] * 32; + Color c = Colors.Green; + if (_bestWeightIdx == i) { - Vector2 vec = _weightDirs[i] * _weights[i] * 32; - Color c = Colors.Green; - if (_bestWeightIdx == i) - { - c = Colors.Blue; - } - else if (_weights[i] < 0) - { - c = Colors.Red; - vec = -vec; - } - DrawLine(Vector2.Zero, vec, c); + c = Colors.Blue; } + else if (_weights[i] < 0) + { + c = Colors.Red; + vec = -vec; + } + DrawLine(Vector2.Zero, vec, c); + } #endif - base._Draw(); - } + base._Draw(); + } - protected virtual Character FindBestTarget() + protected virtual Character FindBestTarget() + { + float bestDist = float.MaxValue; + Character bestChar = null; + foreach (Node node in GetParent().GetChildren()) { - float bestDist = float.MaxValue; - Character bestChar = null; - foreach (Node node in GetParent().GetChildren()) + if (node is Character character && character.Faction != Faction) { - if (node is Character character && character.Faction != Faction) + float dist = Position.DistanceTo(character.Position); + if (dist < bestDist) { - float dist = Position.DistanceTo(character.Position); - if (dist < bestDist) - { - bestDist = dist; - bestChar = character; - } + bestDist = dist; + bestChar = character; } } - return bestChar; } + return bestChar; + } - public void ThinkProcess(double delta) + public void ThinkProcess(double delta) + { + if ((_thinkTimeElapsed += delta) > ThinkTime) { - if ((_thinkTimeElapsed += delta) > ThinkTime) - { - _thinkTimeElapsed = 0; - Think(); + _thinkTimeElapsed = 0; + Think(); #if DEBUG - QueueRedraw(); + QueueRedraw(); #endif - } - - Direction = _weightDirs[_bestWeightIdx]; } - public void UpdateWeights(Vector2 pos) + Direction = _weightDirs[_bestWeightIdx]; + } + + public void UpdateWeights(Vector2 pos) + { + // FIXME: TODO: remove all the spaghetti + Vector2 dir = Target.Normalized(); + float distSq = GlobalPosition.DistanceSquaredTo(pos); + + var spaceState = GetWorld2D().DirectSpaceState; + var exclude = new Godot.Collections.Array(); + exclude.Add(this.GetRid()); + + // calculate weights based on distance + for (int i = 0; i < 16; i++) { - // FIXME: TODO: remove all the spaghetti - Vector2 dir = Target.Normalized(); - float distSq = GlobalPosition.DistanceSquaredTo(pos); + float directDot = _weightDirs[i].Dot(dir); + // clamp dot from [-1, 1] to [0, 1] + directDot = (directDot + 1) / 2; - var spaceState = GetWorld2D().DirectSpaceState; - var exclude = new Godot.Collections.Array(); - exclude.Add(this.GetRid()); + float strafeDot = Math.Abs(_weightDirs[i].Dot(dir.Clockwise90())); + float currDirDot = (_weightDirs[i].Dot(Direction) + 1) / 16; + strafeDot = Mathf.Pow((strafeDot + 1) / 2, 2) + currDirDot; - // calculate weights based on distance - for (int i = 0; i < 16; i++) + // favor strafing when getting closer + if (distSq > _preferredWeightDistanceSq) { - float directDot = _weightDirs[i].Dot(dir); - // clamp dot from [-1, 1] to [0, 1] - directDot = (directDot + 1) / 2; - - float strafeDot = Math.Abs(_weightDirs[i].Dot(dir.Clockwise90())); - float currDirDot = (_weightDirs[i].Dot(Direction) + 1) / 16; - strafeDot = Mathf.Pow((strafeDot + 1) / 2, 2) + currDirDot; - - // favor strafing when getting closer - if (distSq > _preferredWeightDistanceSq) - { - _weights[i] = directDot; - } - else if (distSq > _maxWeightDistanceSq) - { - float dDotWeight = Mathf.Sqrt(distSq / 4096); - float sDotWeight = 1 - dDotWeight; - _weights[i] = (dDotWeight * directDot) + - (sDotWeight * strafeDot); - } - else - { - _weights[i] = strafeDot; - } + _weights[i] = directDot; } - - // subtract weights that collide - for (int i = 0; i < 16; i++) + else if (distSq > _maxWeightDistanceSq) { - var rayParams = new PhysicsRayQueryParameters2D - { - Exclude = exclude, - CollideWithBodies = true, - From = GlobalPosition, - To = GlobalPosition + (_weightDirs[i] * 24), - CollisionMask = 1 + 2 + 16 - }; + float dDotWeight = Mathf.Sqrt(distSq / 4096); + float sDotWeight = 1 - dDotWeight; + _weights[i] = (dDotWeight * directDot) + + (sDotWeight * strafeDot); + } + else + { + _weights[i] = strafeDot; + } + } - var result = spaceState.IntersectRay(rayParams); + // subtract weights that collide + for (int i = 0; i < 16; i++) + { + var rayParams = new PhysicsRayQueryParameters2D + { + Exclude = exclude, + CollideWithBodies = true, + From = GlobalPosition, + To = GlobalPosition + (_weightDirs[i] * 24), + CollisionMask = 1 + 2 + 16 + }; - // if we hit something - if (result.Count > 0) + var result = spaceState.IntersectRay(rayParams); + + // if we hit something + if (result.Count > 0) + { + // then we subtract the value of this from the other weights + float oldWeight = _weights[i]; + for (int j = 0; j < 16; j++) { - // then we subtract the value of this from the other weights - float oldWeight = _weights[i]; - for (int j = 0; j < 16; j++) + if (i == j) { - if (i == j) - { - _weights[i] = 0; - } - else - { - float dot = _weightDirs[i].Dot(_weightDirs[j]); - _weights[j] -= _weights[j] * dot; - } + _weights[i] = 0; + } + else + { + float dot = _weightDirs[i].Dot(_weightDirs[j]); + _weights[j] -= _weights[j] * dot; } - } - } - - - float bestWeight = 0; - for (int i = 0; i < 16; i++) - { - if (_weights[i] > bestWeight) - { - _bestWeightIdx = i; - bestWeight = _weights[i]; } } } - protected virtual void Think() - { - // TODO: the entity should wander if it doesn't find a best target - Character bestTarget = FindBestTarget(); - if (bestTarget is not null) - { - Vector2 pos = FindBestTarget().GlobalPosition; - Target = pos - GlobalPosition; - Vector2 dir = Target; - float dist = GlobalPosition.DistanceSquaredTo(pos); - UpdateWeights(pos); - if (dist < 1024) + float bestWeight = 0; + for (int i = 0; i < 16; i++) + { + if (_weights[i] > bestWeight) + { + _bestWeightIdx = i; + bestWeight = _weights[i]; + } + } + } + + protected virtual void Think() + { + // TODO: the entity should wander if it doesn't find a best target + Character bestTarget = FindBestTarget(); + if (bestTarget is not null) + { + Vector2 pos = FindBestTarget().GlobalPosition; + Target = pos - GlobalPosition; + Vector2 dir = Target; + float dist = GlobalPosition.DistanceSquaredTo(pos); + UpdateWeights(pos); + + if (dist < 1024) + { + if (Inventory.SelectedItem is Weapon weapon) { - if (Inventory.SelectedItem is Weapon weapon) - { - UseCurrentItem(); - } + UseCurrentItem(); } } } diff --git a/Characters/Player.cs b/Characters/Player.cs index 71a1906..755fbab 100644 --- a/Characters/Player.cs +++ b/Characters/Player.cs @@ -1,65 +1,64 @@ using Godot; using SupaLidlGame.Utils; -namespace SupaLidlGame.Characters +namespace SupaLidlGame.Characters; + +public partial class Player : Character { - public partial class Player : Character + private AnimatedSprite2D _sprite; + private string _spriteAnim; + + [Export] + public PlayerCamera Camera { get; set; } + + public string Animation { - private AnimatedSprite2D _sprite; - private string _spriteAnim; - - [Export] - public PlayerCamera Camera { get; set; } - - public string Animation + get => _sprite.Animation; + set { - 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) { - // 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.Play(value); - } - } - - public override void _Ready() - { - _sprite = GetNode("Sprite"); - if (_spriteAnim != default) - { - _sprite.Animation = _spriteAnim; - } - } - - public override void ModifyVelocity() - { - if (StateMachine.CurrentState is SupaLidlGame.State.Character.PlayerRollState) - { - Velocity *= 2; + _spriteAnim = value; + return; } - base.ModifyVelocity(); - } - - public override void Stun(float time) - { - base.Stun(time); - Camera.Shake(2, 0.8f); - // TODO: implement visual effects for stun - } - - public override void Die() - { - GD.Print("died"); - //base.Die(); + _sprite.Play(value); } } + + public override void _Ready() + { + _sprite = GetNode("Sprite"); + if (_spriteAnim != default) + { + _sprite.Animation = _spriteAnim; + } + } + + public override void ModifyVelocity() + { + if (StateMachine.CurrentState is SupaLidlGame.State.Character.PlayerRollState) + { + Velocity *= 2; + } + + base.ModifyVelocity(); + } + + public override void Stun(float time) + { + base.Stun(time); + Camera.Shake(2, 0.8f); + // TODO: implement visual effects for stun + } + + public override void Die() + { + GD.Print("died"); + //base.Die(); + } } diff --git a/Characters/Thinkers/Thinker.cs b/Characters/Thinkers/Thinker.cs index fc0d812..ea27d1c 100644 --- a/Characters/Thinkers/Thinker.cs +++ b/Characters/Thinkers/Thinker.cs @@ -1,10 +1,9 @@ -namespace SupaLidlGame.Characters.Thinkers -{ - public class Thinker - { - public virtual void Think() - { +namespace SupaLidlGame.Characters.Thinkers; + +public class Thinker +{ + public virtual void Think() + { - } } } diff --git a/Entities/Campfire.cs b/Entities/Campfire.cs index 5fae800..5572dbb 100644 --- a/Entities/Campfire.cs +++ b/Entities/Campfire.cs @@ -1,24 +1,23 @@ using Godot; using System; -namespace SupaLidlGame.Entities +namespace SupaLidlGame.Entities; + +public partial class Campfire : StaticBody2D { - public partial class Campfire : StaticBody2D + private PointLight2D _light; + + [Signal] + public delegate void OnCampfireUseEventHandler(); + + public override void _Ready() { - private PointLight2D _light; + _light = GetNode("PointLight2D"); + } - [Signal] - public delegate void OnCampfireUseEventHandler(); - - public override void _Ready() - { - _light = GetNode("PointLight2D"); - } - - public override void _Process(double delta) - { - _light.Energy += (GD.Randf() - 0.5f) * 8 * (float)delta; - _light.Energy = Math.Clamp(_light.Energy, 1.2f, 2.0f); - } + public override void _Process(double delta) + { + _light.Energy += (GD.Randf() - 0.5f) * 8 * (float)delta; + _light.Energy = Math.Clamp(_light.Energy, 1.2f, 2.0f); } } diff --git a/Entities/Projectile.cs b/Entities/Projectile.cs index 8e157c8..06c1f71 100644 --- a/Entities/Projectile.cs +++ b/Entities/Projectile.cs @@ -2,51 +2,50 @@ using Godot; using SupaLidlGame.Characters; using SupaLidlGame.BoundingBoxes; -namespace SupaLidlGame.Entities +namespace SupaLidlGame.Entities; + +public partial class Projectile : RigidBody2D { - public partial class Projectile : RigidBody2D + public Vector2 Velocity => Direction * Speed; + + [Export] + public float Speed { get; set; } + + [Export] + public Vector2 Direction { get; set; } + + [Export] + public Hitbox Hitbox { get; set; } + + [Export] + public double Lifetime { get; set; } = 10; + + public Character Character { get; set; } + + public override void _Process(double delta) { - public Vector2 Velocity => Direction * Speed; - - [Export] - public float Speed { get; set; } - - [Export] - public Vector2 Direction { get; set; } - - [Export] - public Hitbox Hitbox { get; set; } - - [Export] - public double Lifetime { get; set; } = 10; - - public Character Character { get; set; } - - public override void _Process(double delta) + if ((Lifetime -= delta) <= 0) { - if ((Lifetime -= delta) <= 0) - { - QueueFree(); - } + QueueFree(); } + } - public override void _PhysicsProcess(double delta) - { - Vector2 velocity = Velocity; - MoveAndCollide(velocity * (float)delta); - } + public override void _PhysicsProcess(double delta) + { + Vector2 velocity = Velocity; + MoveAndCollide(velocity * (float)delta); + } - public void _on_hitbox_hit(BoundingBox box) + public void _on_hitbox_hit(BoundingBox box) + { + if (box is Hurtbox hurtbox) { - if (box is Hurtbox hurtbox) - { - hurtbox.InflictDamage( - Hitbox.Damage, - Character, - Hitbox.Knockback, - knockbackVector: Direction - ); - } + hurtbox.InflictDamage( + Hitbox.Damage, + Character, + Hitbox.Knockback, + knockbackVector: Direction + ); } } } diff --git a/Extensions/AudioStreamPlayer2D.cs b/Extensions/AudioStreamPlayer2D.cs index 2842d93..23339e7 100644 --- a/Extensions/AudioStreamPlayer2D.cs +++ b/Extensions/AudioStreamPlayer2D.cs @@ -2,72 +2,71 @@ using Godot; using System; using SupaLidlGame.Utils; -namespace SupaLidlGame.Extensions +namespace SupaLidlGame.Extensions; + +public static class AudioStreamPlayer2DExtensions { - public static class AudioStreamPlayer2DExtensions + public static AudioStreamPlayer2D Clone( + this AudioStreamPlayer2D audio) { - public static AudioStreamPlayer2D Clone( - this AudioStreamPlayer2D audio) + var clone = audio.Duplicate() as AudioStreamPlayer2D; + clone.Finished += () => { - var clone = audio.Duplicate() as AudioStreamPlayer2D; - clone.Finished += () => - { - clone.QueueFree(); - }; - return clone; + clone.QueueFree(); + }; + return clone; + } + + public static AudioStreamPlayer2D On( + this AudioStreamPlayer2D audio, + Node parent) + { + var clone = audio.Clone(); + parent.AddChild(clone); + clone.GlobalPosition = audio.GlobalPosition; + return clone; + } + + public static AudioStreamPlayer2D OnWorld( + this AudioStreamPlayer2D audio) + { + var world = audio.GetTree().Root.GetNode("World/TileMap"); + if (world is null) + { + throw new NullReferenceException("World does not exist"); + } + var clone = audio.On(world); + clone.GlobalPosition = audio.GlobalPosition; + return clone; + } + + public static AudioStreamPlayer2D At( + this AudioStreamPlayer2D audio, + Vector2 globalPosition) + { + var world = audio.GetTree().Root.GetNode("World/TileMap"); + if (world is null) + { + throw new NullReferenceException("World does not exist"); } - public static AudioStreamPlayer2D On( - this AudioStreamPlayer2D audio, - Node parent) + var parent = new Node2D(); + world.AddChild(parent); + parent.GlobalPosition = globalPosition; + + var clone = audio.On(world); + clone.Finished += () => { - var clone = audio.Clone(); - parent.AddChild(clone); - clone.GlobalPosition = audio.GlobalPosition; - return clone; - } + parent.QueueFree(); + }; + return clone; + } - public static AudioStreamPlayer2D OnWorld( - this AudioStreamPlayer2D audio) - { - var world = audio.GetTree().Root.GetNode("World/TileMap"); - if (world is null) - { - throw new NullReferenceException("World does not exist"); - } - var clone = audio.On(world); - clone.GlobalPosition = audio.GlobalPosition; - return clone; - } - - public static AudioStreamPlayer2D At( - this AudioStreamPlayer2D audio, - Vector2 globalPosition) - { - var world = audio.GetTree().Root.GetNode("World/TileMap"); - if (world is null) - { - throw new NullReferenceException("World does not exist"); - } - - var parent = new Node2D(); - world.AddChild(parent); - parent.GlobalPosition = globalPosition; - - var clone = audio.On(world); - clone.Finished += () => - { - parent.QueueFree(); - }; - return clone; - } - - public static AudioStreamPlayer2D WithPitchDeviation( - this AudioStreamPlayer2D audio, - float deviation) - { - audio.PitchScale = (float)GD.Randfn(audio.PitchScale, deviation); - return audio; - } + public static AudioStreamPlayer2D WithPitchDeviation( + this AudioStreamPlayer2D audio, + float deviation) + { + audio.PitchScale = (float)GD.Randfn(audio.PitchScale, deviation); + return audio; } } diff --git a/Extensions/Node.cs b/Extensions/Node.cs index 2defb9c..ee52e80 100644 --- a/Extensions/Node.cs +++ b/Extensions/Node.cs @@ -1,41 +1,40 @@ using Godot; -namespace SupaLidlGame.Extensions +namespace SupaLidlGame.Extensions; + +public static class NodeExtensions { - public static class NodeExtensions + /// + /// Iterates through each ancestor until it finds an ancestor of type + /// T + /// + public static T GetAncestor(this Node node) where T : Node { - /// - /// Iterates through each ancestor until it finds an ancestor of type - /// T - /// - public static T GetAncestor(this Node node) where T : Node + Node parent; + + while ((parent = node.GetParent()) != null) { - Node parent; - - while ((parent = node.GetParent()) != null) + if (parent is T t) { - if (parent is T t) - { - return t; - } - - node = parent; + return t; } - return null; + node = parent; } - /// - /// A version GetNode that returns null rather than cause an - /// exception if the node is not found or is not the same type. - /// - /// - /// null if name does not match - /// a valid Node - /// - public static T GetN(this Node node, string name) where T : Node - { - return node.GetNode(name) as T; - } + return null; + } + + /// + /// A version GetNode that returns null rather than cause an + /// exception if the node is not found or is not the same type. + /// + /// + /// null if name does not match + /// a valid Node + /// + public static T GetN(this Node node, string name) where T : Node + { + return node.GetNode(name) as T; } } diff --git a/Extensions/Node2DExtensions.cs b/Extensions/Node2DExtensions.cs index 1acaf32..694fb45 100644 --- a/Extensions/Node2DExtensions.cs +++ b/Extensions/Node2DExtensions.cs @@ -1,13 +1,12 @@ using Godot; -namespace SupaLidlGame.Extensions +namespace SupaLidlGame.Extensions; + +public static class Node2DExtensions { - public static class Node2DExtensions + public static void RayCast(this Node2D node, Vector2 ray) { - public static void RayCast(this Node2D node, Vector2 ray) - { - //var spaceState = node.GetWorld2d().DirectSpaceState; - //var result = spaceState.IntersectRay(); - } + //var spaceState = node.GetWorld2d().DirectSpaceState; + //var result = spaceState.IntersectRay(); } } diff --git a/Extensions/Vector2.cs b/Extensions/Vector2.cs index 070b22e..e8b2d1f 100644 --- a/Extensions/Vector2.cs +++ b/Extensions/Vector2.cs @@ -1,41 +1,40 @@ using Godot; -namespace SupaLidlGame.Extensions +namespace SupaLidlGame.Extensions; + +public static class Vector2Extensions { - public static class Vector2Extensions + public static Vector2 Midpoint(this Vector2 vector, Vector2 other) { - public static Vector2 Midpoint(this Vector2 vector, Vector2 other) + return new Vector2((vector.X + other.X) / 2, + (vector.Y + other.Y) / 2); + } + + public static Vector2 Midpoints(params Vector2[] vectors) + { + int length = vectors.Length; + float x = 0; + float y = 0; + + for (int i = 0; i < length; i++) { - return new Vector2((vector.X + other.X) / 2, - (vector.Y + other.Y) / 2); + x += vectors[i].X; + y += vectors[i].Y; } - public static Vector2 Midpoints(params Vector2[] vectors) - { - int length = vectors.Length; - float x = 0; - float y = 0; + return new Vector2(x / length, y / length); + } - for (int i = 0; i < length; i++) - { - x += vectors[i].X; - y += vectors[i].Y; - } + /// + /// Returns this vector 90 degrees counter clockwise (x, y) -> (-y, x) + /// + public static Vector2 Counterclockwise90(this Vector2 vector) + { + return new Vector2(-vector.Y, vector.X); + } - return new Vector2(x / length, y / length); - } - - /// - /// Returns this vector 90 degrees counter clockwise (x, y) -> (-y, x) - /// - public static Vector2 Counterclockwise90(this Vector2 vector) - { - return new Vector2(-vector.Y, vector.X); - } - - public static Vector2 Clockwise90(this Vector2 vector) - { - return new Vector2(vector.Y, -vector.X); - } + public static Vector2 Clockwise90(this Vector2 vector) + { + return new Vector2(vector.Y, -vector.X); } } diff --git a/Items/Inventory.cs b/Items/Inventory.cs index 5466103..fe50d2f 100644 --- a/Items/Inventory.cs +++ b/Items/Inventory.cs @@ -2,145 +2,144 @@ using Godot; using SupaLidlGame.Characters; using Godot.Collections; -namespace SupaLidlGame.Items +namespace SupaLidlGame.Items; + +public partial class Inventory : Node2D { - public partial class Inventory : Node2D + public Character Character { get; private set; } + + [Export] + public Array Items { get; private set; } + + [Export] + public Dictionary InventoryMap { get; set; } + + public const int MaxCapacity = 32; + + private Item _selectedItem; + + private Item _offhandItem; + + public Item SelectedItem { - public Character Character { get; private set; } + get => _selectedItem; + set => EquipItem(value, ref _selectedItem); + } - [Export] - public Array Items { get; private set; } + public Item OffhandItem + { + get => _selectedItem; + set => EquipItem(value, ref _offhandItem); + } - [Export] - public Dictionary InventoryMap { get; set; } + public bool IsUsingItem => (SelectedItem?.IsUsing ?? false) || + (OffhandItem?.IsUsing ?? false); - public const int MaxCapacity = 32; + public Inventory() + { + InventoryMap = new Dictionary(); + InventoryMap.Add("equip_1", 0); + InventoryMap.Add("equip_2", 1); + InventoryMap.Add("equip_3", 2); + } - private Item _selectedItem; - - private Item _offhandItem; - - public Item SelectedItem + public override void _Ready() + { + if (Items is null) { - get => _selectedItem; - set => EquipItem(value, ref _selectedItem); + // instantiating a new array will prevent characters from + // sharing inventories + Items = new Array(); } - - public Item OffhandItem + Character = GetParent(); + foreach (Node child in GetChildren()) { - get => _selectedItem; - set => EquipItem(value, ref _offhandItem); - } - - public bool IsUsingItem => (SelectedItem?.IsUsing ?? false) || - (OffhandItem?.IsUsing ?? false); - - public Inventory() - { - InventoryMap = new Dictionary(); - InventoryMap.Add("equip_1", 0); - InventoryMap.Add("equip_2", 1); - InventoryMap.Add("equip_3", 2); - } - - public override void _Ready() - { - if (Items is null) + if (child is Item item) { - // instantiating a new array will prevent characters from - // sharing inventories - Items = new Array(); + GD.Print("Adding item " + item.Name); + AddItem(item); } - Character = GetParent(); - foreach (Node child in GetChildren()) - { - if (child is Item item) - { - GD.Print("Adding item " + item.Name); - AddItem(item); - } - } - base._Ready(); } + base._Ready(); + } - private bool EquipItem(Item item, ref Item slot) + private bool EquipItem(Item item, ref Item slot) + { + if (item is not null) { - if (item is not null) + if (item.IsOneHanded) { - if (item.IsOneHanded) + // we can not equip this if either hand is occupied by + // two-handed item + + if (_selectedItem is not null && !_selectedItem.IsOneHanded) { - // we can not equip this if either hand is occupied by - // two-handed item - - if (_selectedItem is not null && !_selectedItem.IsOneHanded) - { - return false; - } - - if (_offhandItem is not null && !_offhandItem.IsOneHanded) - { - return false; - } + return false; } - if (!Items.Contains(item)) + if (_offhandItem is not null && !_offhandItem.IsOneHanded) { - GD.PrintErr("Tried to equip an item not in the inventory."); return false; } } - if (slot is not null) + if (!Items.Contains(item)) { - slot.Unequip(Character); + GD.PrintErr("Tried to equip an item not in the inventory."); + return false; } - - slot = item; - - if (item is not null) - { - item.Equip(Character); - } - - return true; } - public Item GetItemByMap(string keymap) + if (slot is not null) { - if (InventoryMap.ContainsKey(keymap)) + slot.Unequip(Character); + } + + slot = item; + + if (item is not null) + { + item.Equip(Character); + } + + return true; + } + + public Item GetItemByMap(string keymap) + { + if (InventoryMap.ContainsKey(keymap)) + { + int idx = InventoryMap[keymap]; + if (idx < Items.Count) { - int idx = InventoryMap[keymap]; - if (idx < Items.Count) - { - return Items[InventoryMap[keymap]]; - } + return Items[InventoryMap[keymap]]; } - else GD.Print(keymap + " does not exist"); + } + else GD.Print(keymap + " does not exist"); + return null; + } + + public Item AddItem(Item item) + { + if (Items.Count >= MaxCapacity) + { return null; } - public Item AddItem(Item item) + item.CharacterOwner = Character; + item.Visible = false; + if (!Items.Contains(item)) { - if (Items.Count >= MaxCapacity) - { - return null; - } - - item.CharacterOwner = Character; - item.Visible = false; - if (!Items.Contains(item)) - { - Items.Add(item); - } - return item; + Items.Add(item); } + return item; + } - public Item DropItem(Item item) - { - item.CharacterOwner = null; - item.Visible = true; - var e = SelectedItem = item; - throw new System.NotImplementedException(); - } + public Item DropItem(Item item) + { + item.CharacterOwner = null; + item.Visible = true; + var e = SelectedItem = item; + throw new System.NotImplementedException(); } } diff --git a/Items/Item.cs b/Items/Item.cs index f19a768..d717582 100644 --- a/Items/Item.cs +++ b/Items/Item.cs @@ -1,53 +1,52 @@ using Godot; using SupaLidlGame.Characters; -namespace SupaLidlGame.Items +namespace SupaLidlGame.Items; + +public abstract partial class Item : Node2D { - public abstract partial class Item : Node2D + [Export] + public string ItemName { get; set; } + + [Export] + public string Description { get; set; } + + [Export] + public bool CanStack { get; set; } = false; + + public int Count { get; set; } = 1; + + public bool IsOneHanded { get; set; } = false; + + public Character CharacterOwner { get; set; } + + public virtual bool IsUsing => false; + + /// + /// Determines if this item can directly stack with other items + /// + public virtual bool StacksWith(Item item) { - [Export] - public string ItemName { get; set; } - - [Export] - public string Description { get; set; } - - [Export] - public bool CanStack { get; set; } = false; - - public int Count { get; set; } = 1; - - public bool IsOneHanded { get; set; } = false; - - public Character CharacterOwner { get; set; } - - public virtual bool IsUsing => false; - - /// - /// Determines if this item can directly stack with other items - /// - public virtual bool StacksWith(Item item) + if (!CanStack) { - if (!CanStack) - { - return false; - } - - if (ItemName != item.ItemName) - { - return false; - } - - // several more conditions may be added soon - - return true; + return false; } - public abstract void Equip(Character character); + if (ItemName != item.ItemName) + { + return false; + } - public abstract void Unequip(Character character); + // several more conditions may be added soon - public abstract void Use(); - - public abstract void Deuse(); + return true; } + + public abstract void Equip(Character character); + + public abstract void Unequip(Character character); + + public abstract void Use(); + + public abstract void Deuse(); } diff --git a/Items/Weapon.cs b/Items/Weapon.cs index d8485b9..2d6a497 100644 --- a/Items/Weapon.cs +++ b/Items/Weapon.cs @@ -2,109 +2,108 @@ using Godot; using SupaLidlGame.BoundingBoxes; using SupaLidlGame.Characters; -namespace SupaLidlGame.Items +namespace SupaLidlGame.Items; + +public abstract partial class Weapon : Item { - public abstract partial class Weapon : Item + public double RemainingUseTime { get; protected set; } = 0; + + public override bool IsUsing => RemainingUseTime > 0; + + /// + /// How much damage in HP that this weapon deals. + /// + [Export] + public float Damage { get; set; } = 0; + + /// + /// The time in seconds it takes for this weapon to become available + /// again after using. + /// + [Export] + public double UseTime { get; set; } = 0; + + /// + /// The magnitude of the knockback force of the weapon. + /// + [Export] + public float Knockback { get; set; } = 0; + + /// + /// The initial velocity of any projectile the weapon may spawn. + /// + [Export] + public float InitialVelocity { get; set; } = 0; + + /// + /// Hides the weapon if it is idle (i.e. not being used). + /// + [Export] + public bool ShouldHideIdle { get; set; } = false; + + /// + /// Freezes the player's target angle if this weapon is being used. + /// + [Export] + public bool ShouldFreezeAngleOnUse { get; set; } = true; + + [Export] + public float MinDistanceHint { get; set; } + + [Export] + public float MaxDistanceHint { get; set; } + + public virtual bool IsParryable { get; protected set; } = false; + + public bool IsParried { get; set; } + + public Character Character { get; set; } + + public Vector2 UseDirection { get; set; } + + public override bool StacksWith(Item item) => false; + + public override void Equip(Character character) { - public double RemainingUseTime { get; protected set; } = 0; - - public override bool IsUsing => RemainingUseTime > 0; - - /// - /// How much damage in HP that this weapon deals. - /// - [Export] - public float Damage { get; set; } = 0; - - /// - /// The time in seconds it takes for this weapon to become available - /// again after using. - /// - [Export] - public double UseTime { get; set; } = 0; - - /// - /// The magnitude of the knockback force of the weapon. - /// - [Export] - public float Knockback { get; set; } = 0; - - /// - /// The initial velocity of any projectile the weapon may spawn. - /// - [Export] - public float InitialVelocity { get; set; } = 0; - - /// - /// Hides the weapon if it is idle (i.e. not being used). - /// - [Export] - public bool ShouldHideIdle { get; set; } = false; - - /// - /// Freezes the player's target angle if this weapon is being used. - /// - [Export] - public bool ShouldFreezeAngleOnUse { get; set; } = true; - - [Export] - public float MinDistanceHint { get; set; } - - [Export] - public float MaxDistanceHint { get; set; } - - public virtual bool IsParryable { get; protected set; } = false; - - public bool IsParried { get; set; } - - public Character Character { get; set; } - - public Vector2 UseDirection { get; set; } - - public override bool StacksWith(Item item) => false; - - public override void Equip(Character character) + if (!ShouldHideIdle || IsUsing) { - if (!ShouldHideIdle || IsUsing) + Visible = true; + } + Character = character; + } + + public override void Unequip(Character character) + { + Visible = false; + 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) { - Visible = true; - } - Character = character; - } - - public override void Unequip(Character character) - { - Visible = false; - 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(); - } - } - } - - public virtual void _on_hitbox_hit(BoundingBox box) - { - if (box is Hurtbox hurtbox) - { - hurtbox.InflictDamage(Damage, Character, Knockback); + //Deuse(); } } } + + public virtual void _on_hitbox_hit(BoundingBox box) + { + if (box is Hurtbox hurtbox) + { + hurtbox.InflictDamage(Damage, Character, Knockback); + } + } } diff --git a/Items/Weapons/IParryable.cs b/Items/Weapons/IParryable.cs index 49b9a4c..de14b5e 100644 --- a/Items/Weapons/IParryable.cs +++ b/Items/Weapons/IParryable.cs @@ -1,10 +1,9 @@ -namespace SupaLidlGame.Items.Weapons +namespace SupaLidlGame.Items.Weapons; + +public interface IParryable { - public interface IParryable - { - public bool IsParryable { get; } - public bool IsParried { get; } - public ulong ParryTimeOrigin { get; } - public void Stun(); - } + public bool IsParryable { get; } + public bool IsParried { get; } + public ulong ParryTimeOrigin { get; } + public void Stun(); } diff --git a/Items/Weapons/Railgun.cs b/Items/Weapons/Railgun.cs index 5946d67..4ebed2a 100644 --- a/Items/Weapons/Railgun.cs +++ b/Items/Weapons/Railgun.cs @@ -1,22 +1,21 @@ using Godot; using SupaLidlGame.Extensions; -namespace SupaLidlGame.Items.Weapons +namespace SupaLidlGame.Items.Weapons; + +public partial class Railgun : Ranged { - public partial class Railgun : Ranged + public override void Attack() { - public override void Attack() - { - // create projectile - PackedScene scene = GD.Load("res://Entities/RailBeam.tscn"); - GD.Print("lol"); - var projectile = scene.Instantiate(); - projectile.Hitbox.Faction = Character.Faction; - projectile.Direction = Character.Target; - projectile.GlobalPosition = GlobalPosition; - projectile.GlobalRotation = projectile.Direction.Angle(); - this.GetAncestor() - .Entities.AddChild(projectile); - } + // create projectile + PackedScene scene = GD.Load("res://Entities/RailBeam.tscn"); + GD.Print("lol"); + var projectile = scene.Instantiate(); + projectile.Hitbox.Faction = Character.Faction; + projectile.Direction = Character.Target; + projectile.GlobalPosition = GlobalPosition; + projectile.GlobalRotation = projectile.Direction.Angle(); + this.GetAncestor() + .Entities.AddChild(projectile); } } diff --git a/Items/Weapons/Ranged.cs b/Items/Weapons/Ranged.cs index 79dbbd7..06af707 100644 --- a/Items/Weapons/Ranged.cs +++ b/Items/Weapons/Ranged.cs @@ -1,43 +1,42 @@ using Godot; -namespace SupaLidlGame.Items.Weapons +namespace SupaLidlGame.Items.Weapons; + +public abstract partial class Ranged : Weapon { - public abstract partial class Ranged : Weapon + [Export] + public float AngleDeviation { get; set; } + + [Export] + public float ChargeTime { get; set; } + + [Export] + public State.Weapon.WeaponStateMachine StateMachine { get; set; } + + public override bool IsUsing => StateMachine.CurrentState + is State.Weapon.RangedFireState; + + public bool IsChargeable => ChargeTime > 0; + + public bool IsCharging { get; protected set; } + + public override void Use() { - [Export] - public float AngleDeviation { get; set; } - - [Export] - public float ChargeTime { get; set; } - - [Export] - public State.Weapon.WeaponStateMachine StateMachine { get; set; } - - public override bool IsUsing => StateMachine.CurrentState - is State.Weapon.RangedFireState; - - public bool IsChargeable => ChargeTime > 0; - - public bool IsCharging { get; protected set; } - - public override void Use() - { - StateMachine.Use(); - base.Use(); - } - - public override void Deuse() - { - StateMachine.Deuse(); - base.Deuse(); - } - - public override void _Process(double delta) - { - StateMachine.Process(delta); - base._Process(delta); - } - - public abstract void Attack(); + StateMachine.Use(); + base.Use(); } + + public override void Deuse() + { + StateMachine.Deuse(); + base.Deuse(); + } + + public override void _Process(double delta) + { + StateMachine.Process(delta); + base._Process(delta); + } + + public abstract void Attack(); } diff --git a/Items/Weapons/Sword.cs b/Items/Weapons/Sword.cs index 679a830..826116a 100644 --- a/Items/Weapons/Sword.cs +++ b/Items/Weapons/Sword.cs @@ -4,234 +4,233 @@ using SupaLidlGame.Characters; using SupaLidlGame.Extensions; using SupaLidlGame.State.Weapon; -namespace SupaLidlGame.Items.Weapons +namespace SupaLidlGame.Items.Weapons; + +public partial class Sword : Weapon, IParryable { - 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. + /// + /// + /// The value of AttackTime should be less than the + /// value of UseTime + /// + [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 WeaponStateMachine StateMachine { get; set; } + + [Export] + public Node2D Anchor { get; set; } + + public override bool IsParryable { get; protected set; } + + public ulong ParryTimeOrigin { get; protected set; } + + private Tween _currentTween; + + private AnimationNodeStateMachinePlayback _playback; + + public override void Equip(Character character) { - public bool IsAttacking { get; protected set; } + base.Equip(character); + Hitbox.Faction = character.Faction; // character is null before base + } - public override bool IsUsing => StateMachine.CurrentState - is SwordAttackState; + public override void Unequip(Character character) + { + base.Unequip(character); + } - [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. - /// - /// - /// The value of AttackTime should be less than the - /// value of UseTime - /// - [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 WeaponStateMachine StateMachine { get; set; } - - [Export] - public Node2D Anchor { get; set; } - - public override bool IsParryable { get; protected set; } - - public ulong ParryTimeOrigin { get; protected set; } - - private Tween _currentTween; - - private AnimationNodeStateMachinePlayback _playback; - - public override void Equip(Character character) + public override void Use() + { + // we can't use if we're still using the weapon + if (RemainingUseTime > 0) { - base.Equip(character); - Hitbox.Faction = character.Faction; // character is null before base + //return; } - public override void Unequip(Character character) + StateMachine.Use(); + + /* + // reset state of the weapon + IsParried = false; + IsParryable = true; + ParryTimeOrigin = Time.GetTicksMsec(); + + _playback.Travel("use"); + */ + + // play animation depending on rotation of weapon + /* + string anim = "use"; + + if (GetNode("Anchor").Rotation > Mathf.DegToRad(50)) { - base.Unequip(character); + anim = "use2"; } - public override void Use() + if (Character is NPC) { - // we can't use if we're still using the weapon - if (RemainingUseTime > 0) + // NPCs have a slower attack + anim += "-npc"; + } + + 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(); + Deattack(); + base.Deuse(); + } + + public void Attack() + { + //RemainingAttackTime = AttackTime; + IsAttacking = true; + Hitbox.IsDisabled = false; + } + + public void Deattack() + { + IsAttacking = false; + DisableParry(); + Hitbox.IsDisabled = true; + ProcessHits(); + Hitbox.ResetIgnoreList(); + AnimationPlayer.SpeedScale = 1; + } + + 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); + } + + public void ProcessHits() + { + if (IsParried) + { + return; + } + + foreach (BoundingBox box in Hitbox.Hits) + { + GD.Print("processing hit"); + if (box is Hurtbox hurtbox) { - //return; + hurtbox.InflictDamage(Damage, Character, Knockback); } + } + } - StateMachine.Use(); - - /* - // reset state of the weapon - IsParried = false; - IsParryable = true; - ParryTimeOrigin = Time.GetTicksMsec(); - - _playback.Travel("use"); - */ - - // play animation depending on rotation of weapon - /* - string anim = "use"; - - if (GetNode("Anchor").Rotation > Mathf.DegToRad(50)) + public void AttemptParry(Weapon otherWeapon) + { + //if (IsParryable && otherWeapon.IsParryable) + if (otherWeapon.IsParryable && + otherWeapon is IParryable otherParryable) + { + ParryParticles.Emitting = true; + if (ParryTimeOrigin < otherParryable.ParryTimeOrigin) { - anim = "use2"; + // our character was parried } - - if (Character is NPC) + else { - // NPCs have a slower attack - anim += "-npc"; + otherParryable.Stun(); } + } + //this.GetAncestor().AddChild(instance); + } - AnimationPlayer.Play(anim); - */ + public void Stun() + { + IsParried = true; + AnimationPlayer.SpeedScale = 0.25f; + Character.Stun(1.5f); + GetNode("ParrySound").OnWorld().Play(); + } - base.Use(); + public override void _on_hitbox_hit(BoundingBox box) + { + if (IsParried) + { + return; } - public void EnableParry() + if (box is Hitbox hb) { - IsParried = false; - IsParryable = true; - ParryTimeOrigin = Time.GetTicksMsec(); - GD.Print(Character.Name); - } - - public void DisableParry() - { - IsParryable = false; - } - - public override void Deuse() - { - //AnimationPlayer.Stop(); - Deattack(); - base.Deuse(); - } - - public void Attack() - { - //RemainingAttackTime = AttackTime; - IsAttacking = true; - Hitbox.IsDisabled = false; - } - - public void Deattack() - { - IsAttacking = false; - DisableParry(); - Hitbox.IsDisabled = true; - ProcessHits(); - Hitbox.ResetIgnoreList(); - AnimationPlayer.SpeedScale = 1; - } - - 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); - } - - public void ProcessHits() - { - if (IsParried) + Weapon w = hb.GetAncestor(); + if (w is not null) { - return; - } - - foreach (BoundingBox box in Hitbox.Hits) - { - GD.Print("processing hit"); - if (box is Hurtbox hurtbox) - { - hurtbox.InflictDamage(Damage, Character, Knockback); - } + AttemptParry(w); } } - public void AttemptParry(Weapon otherWeapon) + if (box is Hurtbox hurt) { - //if (IsParryable && otherWeapon.IsParryable) - if (otherWeapon.IsParryable && - otherWeapon is IParryable otherParryable) + if (hurt.GetParent() is Character c) { - ParryParticles.Emitting = true; - if (ParryTimeOrigin < otherParryable.ParryTimeOrigin) - { - // our character was parried - } - 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) - { - return; - } - - if (box is Hitbox hb) - { - Weapon w = hb.GetAncestor(); - if (w is not null) + var item = c.Inventory.SelectedItem; + if (item is Weapon w) { AttemptParry(w); } } - - if (box is Hurtbox hurt) - { - if (hurt.GetParent() is Character c) - { - var item = c.Inventory.SelectedItem; - if (item is Weapon w) - { - AttemptParry(w); - } - } - } - } - - protected void SetAnimationCondition(string condition, bool value) - { - AnimationTree.Set("parameters/conditions/" + condition, value); } } + + protected void SetAnimationCondition(string condition, bool value) + { + AnimationTree.Set("parameters/conditions/" + condition, value); + } } diff --git a/Scenes/Level.tscn b/Scenes/Level.tscn index f051216..efb08ee 100644 --- a/Scenes/Level.tscn +++ b/Scenes/Level.tscn @@ -3,7 +3,6 @@ [ext_resource type="Script" path="res://Utils/World.cs" id="1_1k6ew"] [ext_resource type="PackedScene" uid="uid://bxtpv6jqodj4v" path="res://Scenes/Maps/Hills.tscn" id="2_juio7"] -[node name="World" type="Node2D" node_paths=PackedStringArray("CurrentPlayer")] +[node name="World" type="Node2D"] script = ExtResource("1_1k6ew") StartingArea = ExtResource("2_juio7") -CurrentPlayer = NodePath("") diff --git a/Scenes/Map.cs b/Scenes/Map.cs index c4224d9..a51bcdb 100644 --- a/Scenes/Map.cs +++ b/Scenes/Map.cs @@ -1,48 +1,47 @@ using Godot; using System; -namespace SupaLidlGame.Scenes +namespace SupaLidlGame.Scenes; + +public partial class Map : TileMap { - public partial class Map : TileMap + [Export] + public Node2D Entities { get; set; } + + [Export] + public Node2D Areas { get; set; } + + [Export] + public Node2D Spawners { get; set; } + + [Export] + public Vector2 CameraLowerBound { get; set; } + + [Export] + public Vector2 CameraUpperBound { get; set; } + + private bool _active; + + public bool Active { - [Export] - public Node2D Entities { get; set; } - - [Export] - public Node2D Areas { get; set; } - - [Export] - public Node2D Spawners { get; set; } - - [Export] - public Vector2 CameraLowerBound { get; set; } - - [Export] - public Vector2 CameraUpperBound { get; set; } - - private bool _active; - - public bool Active + get { - get - { - return _active; - } - set - { - _active = Visible = value; - SetProcess(value); - } + return _active; } - - public override void _Ready() + set { - Active = true; - } - - public override void _Process(double delta) - { - base._Process(delta); + _active = Visible = value; + SetProcess(value); } } + + public override void _Ready() + { + Active = true; + } + + public override void _Process(double delta) + { + base._Process(delta); + } } diff --git a/State/Character/CharacterState.cs b/State/Character/CharacterState.cs index 8b22a14..08fc94e 100644 --- a/State/Character/CharacterState.cs +++ b/State/Character/CharacterState.cs @@ -1,70 +1,69 @@ using Godot; -namespace SupaLidlGame.State.Character +namespace SupaLidlGame.State.Character; + +public abstract partial class CharacterState : Node, IState { - public abstract partial class CharacterState : Node, IState + [Export] + public Characters.Character Character { get; set; } + + public virtual IState Enter(IState prev) => null; + + public virtual void Exit(IState next) { - [Export] - public Characters.Character Character { get; set; } - public virtual IState Enter(IState prev) => null; - - public virtual void Exit(IState next) - { - - } - - public virtual CharacterState Process(double delta) - { - if (Character.StunTime > 0) - { - Character.StunTime -= delta; - } - - var item = Character.Inventory.SelectedItem; - var offhand = Character.Inventory.OffhandItem; - - // angle towards item use angle or offhand use angle if not used - - bool targetTowards(Items.Item item) - { - if (item is Items.Weapon weapon) - { - if (weapon.IsUsing) - { - Character.Target = weapon.UseDirection; - return true; - } - } - return false; - } - - var _ = targetTowards(item) || targetTowards(offhand); - - return null; - } - - public virtual CharacterState PhysicsProcess(double delta) - { - Character.Velocity = Character.NetImpulse; - - if (Character.NetImpulse.LengthSquared() < Mathf.Pow(Character.Speed, 2)) - { - Character.Velocity += Character.Direction.Normalized() - * Character.Speed; - // we should only modify velocity that is in the player's control - Character.ModifyVelocity(); - } - - Character.NetImpulse = Character.NetImpulse.MoveToward( - Vector2.Zero, - (float)delta * Character.Speed * Character.Friction); - - Character.MoveAndSlide(); - - return null; - } - - public virtual CharacterState Input(InputEvent @event) => null; } + + public virtual CharacterState Process(double delta) + { + if (Character.StunTime > 0) + { + Character.StunTime -= delta; + } + + var item = Character.Inventory.SelectedItem; + var offhand = Character.Inventory.OffhandItem; + + // angle towards item use angle or offhand use angle if not used + + bool targetTowards(Items.Item item) + { + if (item is Items.Weapon weapon) + { + if (weapon.IsUsing) + { + Character.Target = weapon.UseDirection; + return true; + } + } + return false; + } + + var _ = targetTowards(item) || targetTowards(offhand); + + return null; + } + + public virtual CharacterState PhysicsProcess(double delta) + { + Character.Velocity = Character.NetImpulse; + + if (Character.NetImpulse.LengthSquared() < Mathf.Pow(Character.Speed, 2)) + { + Character.Velocity += Character.Direction.Normalized() + * Character.Speed; + // we should only modify velocity that is in the player's control + Character.ModifyVelocity(); + } + + Character.NetImpulse = Character.NetImpulse.MoveToward( + Vector2.Zero, + (float)delta * Character.Speed * Character.Friction); + + Character.MoveAndSlide(); + + return null; + } + + public virtual CharacterState Input(InputEvent @event) => null; } diff --git a/State/Character/CharacterStateMachine.cs b/State/Character/CharacterStateMachine.cs index 87cde18..1e2af78 100644 --- a/State/Character/CharacterStateMachine.cs +++ b/State/Character/CharacterStateMachine.cs @@ -1,40 +1,39 @@ using Godot; -namespace SupaLidlGame.State.Character +namespace SupaLidlGame.State.Character; + +public partial class CharacterStateMachine : StateMachine { - public partial class CharacterStateMachine : StateMachine + [Export] + public override CharacterState InitialState { get; set; } + + [Export] + public Characters.Character Character { get; set; } + + public void Process(double delta) { - [Export] - public override CharacterState InitialState { get; set; } - - [Export] - public Characters.Character Character { get; set; } - - public void Process(double delta) + var state = CurrentState.Process(delta); + if (state is CharacterState) { - var state = CurrentState.Process(delta); - if (state is CharacterState) - { - ChangeState(state); - } + ChangeState(state); } + } - public void PhysicsProcess(double delta) + public void PhysicsProcess(double delta) + { + var state = CurrentState.PhysicsProcess(delta); + if (state is CharacterState) { - var state = CurrentState.PhysicsProcess(delta); - if (state is CharacterState) - { - ChangeState(state); - } + ChangeState(state); } + } - public void Input(InputEvent @event) + public void Input(InputEvent @event) + { + var state = CurrentState.Input(@event); + if (state is CharacterState) { - var state = CurrentState.Input(@event); - if (state is CharacterState) - { - ChangeState(state); - } + ChangeState(state); } } } diff --git a/State/Character/NPCIdleState.cs b/State/Character/NPCIdleState.cs index 3024443..617bf06 100644 --- a/State/Character/NPCIdleState.cs +++ b/State/Character/NPCIdleState.cs @@ -1,26 +1,25 @@ using Godot; -namespace SupaLidlGame.State.Character +namespace SupaLidlGame.State.Character; + +public partial class NPCIdleState : NPCState { - public partial class NPCIdleState : NPCState + [Export] + public CharacterState MoveState { get; set; } + + public override CharacterState Process(double delta) { - [Export] - public CharacterState MoveState { get; set; } - - public override CharacterState Process(double delta) + base.Process(delta); + if (Character.Direction.LengthSquared() > 0) { - base.Process(delta); - if (Character.Direction.LengthSquared() > 0) - { - return MoveState; - } - return null; + return MoveState; } + return null; + } - public override IState Enter(IState previousState) - { - Character.Sprite.Play("idle"); - return base.Enter(previousState); - } + public override IState Enter(IState previousState) + { + Character.Sprite.Play("idle"); + return base.Enter(previousState); } } diff --git a/State/Character/NPCMoveState.cs b/State/Character/NPCMoveState.cs index 59653fe..4a6879a 100644 --- a/State/Character/NPCMoveState.cs +++ b/State/Character/NPCMoveState.cs @@ -1,26 +1,25 @@ using Godot; -namespace SupaLidlGame.State.Character +namespace SupaLidlGame.State.Character; + +public partial class NPCMoveState : NPCState { - public partial class NPCMoveState : NPCState + [Export] + public CharacterState IdleState { get; set; } + + public override CharacterState Process(double delta) { - [Export] - public CharacterState IdleState { get; set; } - - public override CharacterState Process(double delta) + base.Process(delta); + if (Character.Direction.LengthSquared() == 0) { - base.Process(delta); - if (Character.Direction.LengthSquared() == 0) - { - return IdleState; - } - return null; + return IdleState; } + return null; + } - public override IState Enter(IState prev) - { - Character.Sprite.Play("move"); - return base.Enter(prev); - } + public override IState Enter(IState prev) + { + Character.Sprite.Play("move"); + return base.Enter(prev); } } diff --git a/State/Character/NPCState.cs b/State/Character/NPCState.cs index c91397a..f74bce7 100644 --- a/State/Character/NPCState.cs +++ b/State/Character/NPCState.cs @@ -1,13 +1,12 @@ -namespace SupaLidlGame.State.Character -{ - public abstract partial class NPCState : CharacterState - { - protected Characters.NPC _npc => Character as Characters.NPC; +namespace SupaLidlGame.State.Character; - public override CharacterState Process(double delta) - { - _npc.ThinkProcess(delta); - return base.Process(delta); - } +public abstract partial class NPCState : CharacterState +{ + protected Characters.NPC _npc => Character as Characters.NPC; + + public override CharacterState Process(double delta) + { + _npc.ThinkProcess(delta); + return base.Process(delta); } } diff --git a/State/Character/PlayerIdleState.cs b/State/Character/PlayerIdleState.cs index 998c6a3..fcc12f4 100644 --- a/State/Character/PlayerIdleState.cs +++ b/State/Character/PlayerIdleState.cs @@ -1,38 +1,37 @@ using Godot; -namespace SupaLidlGame.State.Character +namespace SupaLidlGame.State.Character; + +public partial class PlayerIdleState : PlayerState { - public partial class PlayerIdleState : PlayerState + [Export] + public CharacterState MoveState { get; set; } + + public override IState Enter(IState previousState) { - [Export] - public CharacterState MoveState { get; set; } - - public override IState Enter(IState previousState) + GD.Print("Entered idle state"); + if (previousState is not PlayerMoveState) { - GD.Print("Entered idle state"); - if (previousState is not PlayerMoveState) - { - 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 - // state, so they do not have to manually check if the player - // wants to move to switch to the move state. - return MoveState; - } - } - _player.Animation = "idle"; - return base.Enter(previousState); - } - - public override CharacterState Process(double delta) - { - base.Process(delta); - 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 + // state, so they do not have to manually check if the player + // wants to move to switch to the move state. return MoveState; } - return null; } + _player.Animation = "idle"; + return base.Enter(previousState); + } + + public override CharacterState Process(double delta) + { + base.Process(delta); + if (Character.Direction.LengthSquared() > 0) + { + return MoveState; + } + return null; } } diff --git a/State/Character/PlayerMoveState.cs b/State/Character/PlayerMoveState.cs index 165f6ed..38334e6 100644 --- a/State/Character/PlayerMoveState.cs +++ b/State/Character/PlayerMoveState.cs @@ -1,46 +1,45 @@ using Godot; -namespace SupaLidlGame.State.Character +namespace SupaLidlGame.State.Character; + +public partial class PlayerMoveState : PlayerState { - public partial class PlayerMoveState : PlayerState + [Export] + public PlayerRollState RollState { get; set; } + + public override IState Enter(IState previousState) { - [Export] - public PlayerRollState RollState { get; set; } + Godot.GD.Print("Started moving"); + _player.Animation = "move"; + return base.Enter(previousState); + } - public override IState Enter(IState previousState) + public override CharacterState Process(double delta) + { + base.Process(delta); + if (Character.Direction.LengthSquared() == 0) { - Godot.GD.Print("Started moving"); - _player.Animation = "move"; - return base.Enter(previousState); + return IdleState; } + return null; + } - public override CharacterState Process(double delta) + public override CharacterState Input(InputEvent @event) + { + if (@event.IsActionPressed("roll")) { - base.Process(delta); - if (Character.Direction.LengthSquared() == 0) + if (Character.Inventory.SelectedItem is Items.Weapon weapon) { - return IdleState; - } - return null; - } - - public override CharacterState Input(InputEvent @event) - { - if (@event.IsActionPressed("roll")) - { - if (Character.Inventory.SelectedItem is Items.Weapon weapon) - { - if (!weapon.IsUsing) - { - return RollState; - } - } - else + if (!weapon.IsUsing) { return RollState; } } - return base.Input(@event); + else + { + return RollState; + } } + return base.Input(@event); } } diff --git a/State/Character/PlayerRollState.cs b/State/Character/PlayerRollState.cs index efafbd6..57b6c87 100644 --- a/State/Character/PlayerRollState.cs +++ b/State/Character/PlayerRollState.cs @@ -1,39 +1,38 @@ using Godot; -namespace SupaLidlGame.State.Character +namespace SupaLidlGame.State.Character; + +public partial class PlayerRollState : PlayerState { - public partial class PlayerRollState : PlayerState + private double _timeLeftToRoll = 0; + + private Vector2 _rollDirection = Vector2.Zero; + + public override IState Enter(IState previousState) { - private double _timeLeftToRoll = 0; + _timeLeftToRoll = 0.5; + // roll the direction we were previously moving in + _rollDirection = Character.Direction; + Character.Target = Character.Direction; + return base.Enter(previousState); + } - private Vector2 _rollDirection = Vector2.Zero; + public override void Exit(IState nextState) + { + // we want to reset our state variables in case we are forced out of + // this state (e.g. from death) + _timeLeftToRoll = 0; + _rollDirection = Character.Direction; + base.Exit(nextState); + } - public override IState Enter(IState previousState) + public override CharacterState Process(double delta) + { + Character.Direction = _rollDirection; + if ((_timeLeftToRoll -= delta) <= 0) { - _timeLeftToRoll = 0.5; - // roll the direction we were previously moving in - _rollDirection = Character.Direction; - Character.Target = Character.Direction; - return base.Enter(previousState); - } - - public override void Exit(IState nextState) - { - // we want to reset our state variables in case we are forced out of - // this state (e.g. from death) - _timeLeftToRoll = 0; - _rollDirection = Character.Direction; - base.Exit(nextState); - } - - public override CharacterState Process(double delta) - { - Character.Direction = _rollDirection; - if ((_timeLeftToRoll -= delta) <= 0) - { - return IdleState; - } - return null; + return IdleState; } + return null; } } diff --git a/State/Character/PlayerState.cs b/State/Character/PlayerState.cs index f2ce8c7..648d2e7 100644 --- a/State/Character/PlayerState.cs +++ b/State/Character/PlayerState.cs @@ -1,72 +1,71 @@ using Godot; using SupaLidlGame.Characters; -namespace SupaLidlGame.State.Character +namespace SupaLidlGame.State.Character; + +public abstract partial class PlayerState : CharacterState { - public abstract partial class PlayerState : CharacterState + protected Player _player => Character as Player; + + [Export] + public PlayerIdleState IdleState { get; set; } + + public override CharacterState Input(InputEvent @event) { - protected Player _player => Character as Player; + var inventory = Character.Inventory; - [Export] - public PlayerIdleState IdleState { get; set; } - - public override CharacterState Input(InputEvent @event) + if (this is PlayerIdleState or PlayerMoveState && + !_player.Inventory.IsUsingItem) { - var inventory = Character.Inventory; - - if (this is PlayerIdleState or PlayerMoveState && - !_player.Inventory.IsUsingItem) + if (@event.IsActionPressed("equip_1")) { - if (@event.IsActionPressed("equip_1")) - { - inventory.SelectedItem = inventory.GetItemByMap("equip_1"); - } + inventory.SelectedItem = inventory.GetItemByMap("equip_1"); } - - return base.Input(@event); } - public override CharacterState Process(double delta) + return base.Input(@event); + } + + public override CharacterState Process(double delta) + { + Character.Direction = Godot.Input.GetVector("ui_left", "ui_right", + "ui_up", "ui_down"); + Character.LookTowardsDirection(); + + Vector2 mousePos = Character.GetGlobalMousePosition(); + Vector2 dirToMouse = Character.GlobalPosition.DirectionTo(mousePos); + + bool targetTowards(Items.Item item) { - Character.Direction = Godot.Input.GetVector("ui_left", "ui_right", - "ui_up", "ui_down"); - Character.LookTowardsDirection(); - - Vector2 mousePos = Character.GetGlobalMousePosition(); - Vector2 dirToMouse = Character.GlobalPosition.DirectionTo(mousePos); - - bool targetTowards(Items.Item item) + if (Character.Inventory.SelectedItem is Items.Weapon weapon) { - if (Character.Inventory.SelectedItem is Items.Weapon weapon) + if (!weapon.IsUsing) { - if (!weapon.IsUsing) + var isPressed = Godot.Input.IsActionPressed("attack1"); + var ret = false; + + if (!weapon.ShouldHideIdle || isPressed) { - var isPressed = Godot.Input.IsActionPressed("attack1"); - var ret = false; - - if (!weapon.ShouldHideIdle || isPressed) - { - Character.Target = dirToMouse; - ret = true; - } - - if (isPressed) - { - Character.UseCurrentItem(); - } - - return ret; + Character.Target = dirToMouse; + ret = true; } + + if (isPressed) + { + Character.UseCurrentItem(); + } + + return ret; } - return false; } - - var item = Character.Inventory.SelectedItem; - var offhand = Character.Inventory.OffhandItem; - - var _ = targetTowards(item) || targetTowards(offhand); - - return base.Process(delta); + return false; } + + var item = Character.Inventory.SelectedItem; + var offhand = Character.Inventory.OffhandItem; + + var _ = targetTowards(item) || targetTowards(offhand); + + return base.Process(delta); } } diff --git a/State/IState.cs b/State/IState.cs index 2d920c3..0f301f7 100644 --- a/State/IState.cs +++ b/State/IState.cs @@ -1,23 +1,22 @@ -namespace SupaLidlGame.State +namespace SupaLidlGame.State; + +public interface IState where T : IState { - 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); + /// + /// 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); - /// - /// Called when the Character exits this CharacterState. - /// - public void Exit(IState nextState); + /// + /// Called when the Character exits this CharacterState. + /// + public void Exit(IState nextState); - public IState Process(double delta) => null; - } + public IState Process(double delta) => null; } diff --git a/State/StateMachine.cs b/State/StateMachine.cs index 99b9349..e204cc9 100644 --- a/State/StateMachine.cs +++ b/State/StateMachine.cs @@ -1,41 +1,40 @@ using Godot; -namespace SupaLidlGame.State +namespace SupaLidlGame.State; + +public abstract partial class StateMachine : Node where T : IState { - public abstract partial class StateMachine : Node where T : IState + public T CurrentState { get; protected set; } + + public abstract T InitialState { get; set; } + + public override void _Ready() { - public T CurrentState { get; protected set; } + ChangeState(InitialState); + } - public abstract T InitialState { get; set; } - - public override void _Ready() + public virtual bool ChangeState(T nextState, bool isProxied = false) + { + if (nextState is null) { - ChangeState(InitialState); + return false; } - public virtual bool ChangeState(T nextState, bool isProxied = false) + if (CurrentState is not null) { - if (nextState is null) - { - return false; - } - - if (CurrentState is not null) - { - CurrentState.Exit(nextState); - } - - CurrentState = nextState; - - // if the next state decides it should enter a different state, - // then we enter that different state instead - var nextNextState = nextState.Enter(CurrentState); - if (nextNextState is T t) - { - return ChangeState(t, true); - } - - return true; + CurrentState.Exit(nextState); } + + CurrentState = nextState; + + // if the next state decides it should enter a different state, + // then we enter that different state instead + var nextNextState = nextState.Enter(CurrentState); + if (nextNextState is T t) + { + return ChangeState(t, true); + } + + return true; } } diff --git a/State/Weapon/RangedFireState.cs b/State/Weapon/RangedFireState.cs index 3758672..de65088 100644 --- a/State/Weapon/RangedFireState.cs +++ b/State/Weapon/RangedFireState.cs @@ -1,38 +1,37 @@ using Godot; -namespace SupaLidlGame.State.Weapon +namespace SupaLidlGame.State.Weapon; + +public partial class RangedFireState : WeaponState { - public partial class RangedFireState : WeaponState + [Export] + public Items.Weapons.Ranged Weapon { get; set; } + + [Export] + public RangedIdleState IdleState { get; set; } + + private double _timeLeft = 0; + + public override IState Enter(IState prev) { - [Export] - public Items.Weapons.Ranged Weapon { get; set; } + //_timeLeft + _timeLeft = Weapon.UseTime; + Weapon.Attack(); + Weapon.UseDirection = Weapon.Character.Target; + return null; + } - [Export] - public RangedIdleState IdleState { get; set; } - - private double _timeLeft = 0; - - public override IState Enter(IState prev) + public override WeaponState Process(double delta) + { + if ((_timeLeft -= delta) <= 0) { - //_timeLeft - _timeLeft = Weapon.UseTime; - Weapon.Attack(); - Weapon.UseDirection = Weapon.Character.Target; - return null; + return IdleState; } + return null; + } - public override WeaponState Process(double delta) - { - if ((_timeLeft -= delta) <= 0) - { - return IdleState; - } - return null; - } + public override void Exit(IState nextState) + { - public override void Exit(IState nextState) - { - - } } } diff --git a/State/Weapon/RangedIdleState.cs b/State/Weapon/RangedIdleState.cs index 3a26eff..e887307 100644 --- a/State/Weapon/RangedIdleState.cs +++ b/State/Weapon/RangedIdleState.cs @@ -1,29 +1,28 @@ using Godot; -namespace SupaLidlGame.State.Weapon +namespace SupaLidlGame.State.Weapon; + +public partial class RangedIdleState : WeaponState { - public partial class RangedIdleState : WeaponState + [Export] + public RangedFireState FireState { get; set; } + + [Export] + public Items.Weapons.Ranged Weapon { get; set; } + + public override IState Enter(IState prev) { - [Export] - public RangedFireState FireState { get; set; } + Weapon.Visible = !Weapon.ShouldHideIdle; + return null; + } - [Export] - public Items.Weapons.Ranged Weapon { get; set; } + public override WeaponState Use() + { + return FireState; + } - public override IState Enter(IState prev) - { - Weapon.Visible = !Weapon.ShouldHideIdle; - return null; - } - - public override WeaponState Use() - { - return FireState; - } - - public override void Exit(IState nextState) - { - Weapon.Visible = true; - } + public override void Exit(IState nextState) + { + Weapon.Visible = true; } } diff --git a/State/Weapon/SwordAnticipateState.cs b/State/Weapon/SwordAnticipateState.cs index a40ef3b..f393a65 100644 --- a/State/Weapon/SwordAnticipateState.cs +++ b/State/Weapon/SwordAnticipateState.cs @@ -1,46 +1,45 @@ using Godot; -namespace SupaLidlGame.State.Weapon +namespace SupaLidlGame.State.Weapon; + +public partial class SwordAnticipateState : WeaponState { - public partial class SwordAnticipateState : WeaponState + [Export] + public SupaLidlGame.Items.Weapons.Sword Sword { get; set; } + + [Export] + public SwordAttackState AttackState { get; set; } + + private double _anticipateTime; + + public override WeaponState Enter(IState prevState) { - [Export] - public SupaLidlGame.Items.Weapons.Sword Sword { get; set; } - - [Export] - public SwordAttackState AttackState { get; set; } + Sword.EnableParry(); - private double _anticipateTime; - - public override WeaponState Enter(IState prevState) + if (Sword.Character is SupaLidlGame.Characters.Player) { - Sword.EnableParry(); - - if (Sword.Character is SupaLidlGame.Characters.Player) - { - return AttackState; - } - - if (Sword.Anchor.Rotation > Mathf.DegToRad(50)) - { - Sword.AnimationPlayer.Play("anticipate_alternate"); - } - else - { - Sword.AnimationPlayer.Play("anticipate"); - } - _anticipateTime = Sword.NPCAnticipateTime; - return null; + return AttackState; } - public override WeaponState Process(double delta) + if (Sword.Anchor.Rotation > Mathf.DegToRad(50)) { - // go into attack state if anticipation time is delta - if ((_anticipateTime -= delta) <= 0) - { - return AttackState; - } - return null; + Sword.AnimationPlayer.Play("anticipate_alternate"); } + else + { + Sword.AnimationPlayer.Play("anticipate"); + } + _anticipateTime = Sword.NPCAnticipateTime; + return null; + } + + public override WeaponState Process(double delta) + { + // go into attack state if anticipation time is delta + if ((_anticipateTime -= delta) <= 0) + { + return AttackState; + } + return null; } } diff --git a/State/Weapon/SwordAttackState.cs b/State/Weapon/SwordAttackState.cs index 90d133e..11014c7 100644 --- a/State/Weapon/SwordAttackState.cs +++ b/State/Weapon/SwordAttackState.cs @@ -1,89 +1,88 @@ using Godot; -namespace SupaLidlGame.State.Weapon +namespace SupaLidlGame.State.Weapon; + +public partial class SwordAttackState : WeaponState { - public partial class SwordAttackState : WeaponState + [Export] + public SupaLidlGame.Items.Weapons.Sword Sword { get; set; } + + [Export] + public SwordAnticipateState AnticipateState { get; set; } + + [Export] + public SwordIdleState IdleState { get; set; } + + private double _attackDuration = 0; + + private double _useDuration = 0; + + private double _attackAnimDuration = 0; + + private bool _isAlternate = false; + + public override WeaponState Enter(IState prevState) { - [Export] - public SupaLidlGame.Items.Weapons.Sword Sword { get; set; } + //Sword.AnimationPlayer.Stop(); + Sword.Attack(); - [Export] - public SwordAnticipateState AnticipateState { get; set; } - - [Export] - public SwordIdleState IdleState { get; set; } - - private double _attackDuration = 0; - - private double _useDuration = 0; - - private double _attackAnimDuration = 0; - - private bool _isAlternate = false; - - public override WeaponState Enter(IState prevState) + if (_isAlternate) { - //Sword.AnimationPlayer.Stop(); - Sword.Attack(); - - if (_isAlternate) - { - Sword.AnimationPlayer.Play("attack_alternate"); - } - else - { - Sword.AnimationPlayer.Play("attack"); - } - - _attackDuration = Sword.AttackTime; - _useDuration = Sword.UseTime; - _attackAnimDuration = Sword.AttackAnimationDuration; - - Sword.Visible = true; - Sword.UseDirection = Sword.Character.Target; - return null; + Sword.AnimationPlayer.Play("attack_alternate"); + } + else + { + Sword.AnimationPlayer.Play("attack"); } - public override void Exit(IState nextState) + _attackDuration = Sword.AttackTime; + _useDuration = Sword.UseTime; + _attackAnimDuration = Sword.AttackAnimationDuration; + + Sword.Visible = true; + Sword.UseDirection = Sword.Character.Target; + return null; + } + + public override void Exit(IState nextState) + { + Sword.Deattack(); + } + + public override WeaponState Use() + { + if (_useDuration <= 0) { - Sword.Deattack(); + // if we are still playing the current attack animation, we should alternate + if (_attackAnimDuration > 0) + { + _isAlternate = !_isAlternate; + } + return IdleState; } - public override WeaponState Use() - { - if (_useDuration <= 0) - { - // if we are still playing the current attack animation, we should alternate - if (_attackAnimDuration > 0) - { - _isAlternate = !_isAlternate; - } - return IdleState; - } + return null; + } - return null; + public override WeaponState Process(double delta) + { + if (_attackDuration > 0) + { + if ((_attackDuration -= delta) <= 0) + { + Sword.Deattack(); + } } - public override WeaponState Process(double delta) + if ((_attackAnimDuration -= delta) <= 0) { - if (_attackDuration > 0) - { - if ((_attackDuration -= delta) <= 0) - { - Sword.Deattack(); - } - } - - if ((_attackAnimDuration -= delta) <= 0) - { - Sword.AnimationPlayer.Play("RESET"); - _isAlternate = false; - return IdleState; - } - - _useDuration -= delta; - - return null; + Sword.AnimationPlayer.Play("RESET"); + _isAlternate = false; + return IdleState; } + + _useDuration -= delta; + + return null; } } diff --git a/State/Weapon/SwordIdleState.cs b/State/Weapon/SwordIdleState.cs index 1f5b862..bcaf07e 100644 --- a/State/Weapon/SwordIdleState.cs +++ b/State/Weapon/SwordIdleState.cs @@ -1,52 +1,51 @@ using Godot; -namespace SupaLidlGame.State.Weapon +namespace SupaLidlGame.State.Weapon; + +public partial class SwordIdleState : WeaponState { - public partial class SwordIdleState : WeaponState + [Export] + public SwordAnticipateState AnticipateState { get; set; } + + [Export] + public SupaLidlGame.Items.Weapons.Sword Sword { get; set; } + + private double _attackCooldown; + + public override WeaponState Enter(IState prevState) { - [Export] - public SwordAnticipateState AnticipateState { get; set; } - - [Export] - public SupaLidlGame.Items.Weapons.Sword Sword { get; set; } - - private double _attackCooldown; - - public override WeaponState Enter(IState prevState) + if (prevState is SwordAttackState) { - if (prevState is SwordAttackState) - { - _attackCooldown = Sword.UseTime - Sword.AttackTime; - } - - Sword.Visible = !Sword.ShouldHideIdle; - - return null; + _attackCooldown = Sword.UseTime - Sword.AttackTime; } - public override void Exit(IState nextState) - { - Sword.Visible = true; - } + Sword.Visible = !Sword.ShouldHideIdle; - public override WeaponState Use() - { - if (_attackCooldown <= 0) - { - return AnticipateState; - } + return null; + } + public override void Exit(IState nextState) + { + Sword.Visible = true; + } + + public override WeaponState Use() + { + if (_attackCooldown <= 0) + { return AnticipateState; } - public override WeaponState Process(double delta) - { - if (_attackCooldown > 0) - { - _attackCooldown -= delta; - } + return AnticipateState; + } - return null; + public override WeaponState Process(double delta) + { + if (_attackCooldown > 0) + { + _attackCooldown -= delta; } + + return null; } } diff --git a/State/Weapon/WeaponState.cs b/State/Weapon/WeaponState.cs index 2a817e3..e57582f 100644 --- a/State/Weapon/WeaponState.cs +++ b/State/Weapon/WeaponState.cs @@ -1,20 +1,19 @@ using Godot; -namespace SupaLidlGame.State.Weapon +namespace SupaLidlGame.State.Weapon; + +public abstract partial class WeaponState : Node, IState { - public abstract partial class WeaponState : Node, IState + public virtual WeaponState Use() => null; + + public virtual WeaponState Deuse() => null; + + public abstract IState Enter(IState previousState); + + public virtual void Exit(IState nextState) { - public virtual WeaponState Use() => null; - public virtual WeaponState Deuse() => null; - - public abstract IState Enter(IState previousState); - - public virtual void Exit(IState nextState) - { - - } - - public virtual IState Process(double delta) => null; } + + public virtual IState Process(double delta) => null; } diff --git a/State/Weapon/WeaponStateMachine.cs b/State/Weapon/WeaponStateMachine.cs index 223693b..aefb826 100644 --- a/State/Weapon/WeaponStateMachine.cs +++ b/State/Weapon/WeaponStateMachine.cs @@ -1,37 +1,36 @@ using Godot; -namespace SupaLidlGame.State.Weapon +namespace SupaLidlGame.State.Weapon; + +public partial class WeaponStateMachine : StateMachine { - public partial class WeaponStateMachine : StateMachine + [Export] + public override WeaponState InitialState { get; set; } + + public void Use() { - [Export] - public override WeaponState InitialState { get; set; } - - public void Use() + var state = CurrentState.Use(); + if (state is WeaponState) { - var state = CurrentState.Use(); - if (state is WeaponState) - { - ChangeState(state); - } + ChangeState(state); } + } - public void Deuse() + public void Deuse() + { + var state = CurrentState.Deuse(); + if (state is WeaponState) { - var state = CurrentState.Deuse(); - if (state is WeaponState) - { - ChangeState(state); - } + ChangeState(state); } + } - public void Process(double delta) + public void Process(double delta) + { + var state = CurrentState.Process(delta); + if (state is WeaponState s) { - var state = CurrentState.Process(delta); - if (state is WeaponState s) - { - ChangeState(s); - } + ChangeState(s); } } } diff --git a/Tests/ContextBasedSteering.cs b/Tests/ContextBasedSteering.cs index 17354c7..5d3d611 100644 --- a/Tests/ContextBasedSteering.cs +++ b/Tests/ContextBasedSteering.cs @@ -2,57 +2,56 @@ using Godot; using SupaLidlGame.Extensions; using System; -namespace SupaLidlGame.Prototyping +namespace SupaLidlGame.Prototyping; + +public partial class ContextBasedSteering : Node2D { - public partial class ContextBasedSteering : Node2D + public float PreferredDistance { get; set; } = 256.0f; + Vector2 _direction; + + // Called when the node enters the scene tree for the first time. + public override void _Ready() { - public float PreferredDistance { get; set; } = 256.0f; - Vector2 _direction; + GD.Print("Started ContextBasedSteering test"); + } - // Called when the node enters the scene tree for the first time. - public override void _Ready() - { - GD.Print("Started ContextBasedSteering test"); - } + // Called every frame. 'delta' is the elapsed time since the previous frame. + public override void _Process(double delta) + { + _direction = GetDirection(); + QueueRedraw(); + } - // Called every frame. 'delta' is the elapsed time since the previous frame. - public override void _Process(double delta) - { - _direction = GetDirection(); - QueueRedraw(); - } + public Vector2 GetDirection() + { + float directWeight; + float strafeWeight; - public Vector2 GetDirection() - { - float directWeight; - float strafeWeight; + Vector2 towards = GetGlobalMousePosition() - GlobalPosition; + float dist = towards.Length(); - Vector2 towards = GetGlobalMousePosition() - GlobalPosition; - float dist = towards.Length(); + Vector2 directDir = towards.Normalized(); + Vector2 strafeDir = directDir.Clockwise90(); - Vector2 directDir = towards.Normalized(); - Vector2 strafeDir = directDir.Clockwise90(); + // weights approach 1 + // dy/dx = 1 - y + // y = 1 - e^(-x) - // weights approach 1 - // dy/dx = 1 - y - // y = 1 - e^(-x) + directWeight = 1 - Mathf.Pow(Mathf.E, -(dist / PreferredDistance)); + strafeWeight = 1 - directWeight; - directWeight = 1 - Mathf.Pow(Mathf.E, -(dist / PreferredDistance)); - strafeWeight = 1 - directWeight; + /* + Vector2 midpoint = (strafeDir * strafeWeight) + .Midpoint(directDir * directWeight); + */ + Vector2 midpoint = (directDir * directWeight) + .Midpoint(strafeDir * strafeWeight); - /* - Vector2 midpoint = (strafeDir * strafeWeight) - .Midpoint(directDir * directWeight); - */ - Vector2 midpoint = (directDir * directWeight) - .Midpoint(strafeDir * strafeWeight); + return midpoint.Normalized(); + } - return midpoint.Normalized(); - } - - public override void _Draw() - { - DrawLine(Vector2.Zero, _direction * 256, Colors.Green); - } + public override void _Draw() + { + DrawLine(Vector2.Zero, _direction * 256, Colors.Green); } } diff --git a/UI/FloatingText.cs b/UI/FloatingText.cs index b36c31c..6ccb235 100644 --- a/UI/FloatingText.cs +++ b/UI/FloatingText.cs @@ -1,51 +1,50 @@ using Godot; using System; -namespace SupaLidlGame.UI +namespace SupaLidlGame.UI; + +public partial class FloatingText : Node2D { - public partial class FloatingText : Node2D + private Label _label; + + [Export] + public string Text { get; set; } + + public override void _Ready() { - private Label _label; + _label = GetNode