diff --git a/Characters/Character.cs b/Characters/Character.cs index 7546533..168b31d 100644 --- a/Characters/Character.cs +++ b/Characters/Character.cs @@ -26,9 +26,6 @@ public partial class Character : CharacterBody2D, IFaction } } - [Export] - public float Stealth { get; protected set; } = 0; - [Signal] public delegate void HealthChangedEventHandler(Events.HealthChangedArgs args); @@ -168,6 +165,9 @@ public partial class Character : CharacterBody2D, IFaction } } + /// + /// Handles the Character's death. + /// public virtual void Die() { if (HurtAnimation.HasAnimation("death")) @@ -190,11 +190,20 @@ public partial class Character : CharacterBody2D, IFaction NetImpulse += impulse / Mass; } + /// + /// Stuns the Chararacter for an amount of time. If + /// is less than the Character's current + /// stun time left, it will have no effect. + /// public virtual void Stun(float time) { StunTime = Mathf.Max(time, StunTime); } + /// + /// Draws the character so that its sprite and inventory items face the + /// character's direction. + /// protected virtual void DrawTarget() { Vector2 target = Target; @@ -213,6 +222,10 @@ public partial class Character : CharacterBody2D, IFaction Inventory.Rotation = angle; } + /// + /// Use the current item the character is using. Prefer to call this over + /// Item.Use as it will check if the character is stunned or alive. + /// public void UseCurrentItem() { if (StunTime > 0 || !IsAlive) @@ -264,6 +277,9 @@ public partial class Character : CharacterBody2D, IFaction } } + /// + /// Override this method to modify the damage the character takes. + /// protected virtual float ReceiveDamage( float damage, Character inflictor, @@ -285,6 +301,9 @@ public partial class Character : CharacterBody2D, IFaction _curDamageText.ShowText(); } + /// + /// Handles the character taking damage. + /// protected virtual void OnReceivedDamage( float damage, Character inflictor, @@ -348,6 +367,7 @@ public partial class Character : CharacterBody2D, IFaction } } +#if DEBUG /// /// For debugging purposes /// @@ -355,7 +375,12 @@ public partial class Character : CharacterBody2D, IFaction { OnReceivedDamage(damage, null, 0); } +#endif + /// + /// Plays a footstep sound. This should be called through an + /// AnimationPlayer to sync sounds with animations. + /// public virtual void Footstep() { if (GetNode("Effects/Footstep") is AudioStreamPlayer2D player) @@ -364,6 +389,15 @@ public partial class Character : CharacterBody2D, IFaction } } + /// + /// Returns whether the Character has line of sight with + /// . + /// + /// The character to check for LOS + /// + /// Determines whether the raycast should pass through world clips (physics + /// layer 5) + /// public bool HasLineOfSight(Character character, bool excludeClip = false) { var exclude = new Godot.Collections.Array(); diff --git a/Characters/NPC.cs b/Characters/NPC.cs index 1143e5d..fedf3c0 100644 --- a/Characters/NPC.cs +++ b/Characters/NPC.cs @@ -96,29 +96,9 @@ public partial class NPC : Character }; } - public override void _Draw() - { -#if DEBUG - for (int i = 0; i < 16; 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); - } -#endif - - base._Draw(); - } - + /// + /// Finds the NPC's best character to target. + /// public virtual Character FindBestTarget() { float bestScore = float.MaxValue; @@ -136,21 +116,9 @@ public partial class NPC : Character float score = 0; score += Position.DistanceTo(character.Position); - score *= (character.Stealth + 1); - - // if the character has enough stealth, the dot product of the - // enemy's current direction and to the character will affect - // the score - // TODO: implement if (score < bestScore) { - // if the character has enough stealth, they won't be - // targeted if the NPC is not able to see - if (!HasLineOfSight(character) && character.Stealth >= 1) - { - continue; - } bestScore = score; bestChar = character; } @@ -170,132 +138,4 @@ public partial class NPC : Character ThinkerStateMachine.PhysicsProcess(delta); base._PhysicsProcess(delta); } - - public void ThinkProcess(double delta) - { - if ((_thinkTimeElapsed += delta) > ThinkTime) - { - _thinkTimeElapsed = 0; - Think(); -#if DEBUG_NPC - QueueRedraw(); -#endif - } - - if (!ShouldMove || (!ShouldMoveWhenUsingItem && Inventory.IsUsingItem)) - { - Direction = Vector2.Zero; - } - else - { - 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++) - { - 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; - } - } - - // 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 - }; - - 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++) - { - if (i == j) - { - _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 < 1600 && CanAttack) - { - if (Inventory.SelectedItem is Weapon weapon) - { - UseCurrentItem(); - } - } - } - } } diff --git a/Characters/Player.cs b/Characters/Player.cs index d74a1d2..18702fc 100644 --- a/Characters/Player.cs +++ b/Characters/Player.cs @@ -51,10 +51,6 @@ public sealed partial class Player : Character public override void _Process(double delta) { base._Process(delta); - - var mod = Sprite.SelfModulate; - mod.A = 1 - (Stealth / 2); - Sprite.SelfModulate = mod; } public override void _Input(InputEvent @event) @@ -65,6 +61,9 @@ public sealed partial class Player : Character } } + /// + /// Respawns the player with full health and plays spawn animation + /// public void Spawn() { Health = 100; diff --git a/Items/Item.cs b/Items/Item.cs index df47050..78ee0b3 100644 --- a/Items/Item.cs +++ b/Items/Item.cs @@ -23,6 +23,12 @@ public abstract partial class Item : Node2D public Character CharacterOwner { get; set; } + /// + /// Determines if the item is being used. This property determines if + /// a character can use another item or not. + /// See + /// + /// public virtual bool IsUsing => false; /// diff --git a/Items/Weapons/Sword.cs b/Items/Weapons/Sword.cs index 1578c9f..76a1964 100644 --- a/Items/Weapons/Sword.cs +++ b/Items/Weapons/Sword.cs @@ -7,6 +7,9 @@ using SupaLidlGame.State.Weapon; namespace SupaLidlGame.Items.Weapons; +/// +/// A basic melee weapon. +/// public partial class Sword : Weapon, IParryable { public bool IsAttacking { get; protected set; } @@ -77,6 +80,9 @@ public partial class Sword : Weapon, IParryable EnableParry(Time.GetTicksMsec()); } + /// + /// Makes this melee weapon be able to parry and be parried. + /// public void EnableParry(ulong parryTimeOrigin) { IsParried = false; @@ -84,6 +90,9 @@ public partial class Sword : Weapon, IParryable ParryTimeOrigin = parryTimeOrigin; } + /// + /// Makes this melee weapon be able to parry and be parried. + /// public void DisableParry() { IsParryable = false; @@ -113,6 +122,10 @@ public partial class Sword : Weapon, IParryable base.DeuseAlt(); } + /// + /// Enables the weapon's hitbox. Prefer to call this from a state machine + /// rather than managing state through the weapon script. + /// public void Attack() { //RemainingAttackTime = AttackTime; @@ -120,6 +133,9 @@ public partial class Sword : Weapon, IParryable Hitbox.IsDisabled = false; } + /// + /// Disables the weapon's hitbox and processes all hurtboxes it hit. + /// public void Deattack() { IsAttacking = false; @@ -161,6 +177,9 @@ public partial class Sword : Weapon, IParryable base._Process(delta); } + /// + /// Processes all hits and applies damages to hurtboxes. + /// public void ProcessHits() { if (IsParried) @@ -199,6 +218,10 @@ public partial class Sword : Weapon, IParryable } } + /// + /// Stuns the wepaon holder. This is unique to swords and melee weapons + /// if they can parry. + /// public void Stun() { IsParried = true; @@ -238,9 +261,4 @@ public partial class Sword : Weapon, IParryable } } } - - protected void SetAnimationCondition(string condition, bool value) - { - - } } diff --git a/State/StateMachine.cs b/State/StateMachine.cs index 5be3f54..c680c39 100644 --- a/State/StateMachine.cs +++ b/State/StateMachine.cs @@ -18,11 +18,28 @@ public abstract partial class StateMachine : Node where T : Node, IState ChangeState(InitialState); } + /// + /// Changes the state of the StateMachine. + /// + /// The next state to transition to. + /// + /// if is a + /// valid state, otherwise + /// public virtual bool ChangeState(T nextState) { return ChangeState(nextState, out Stack _); } + /// + /// Changes the state of the StateMachine. + /// + /// The next state to transition to. + /// The actual state. + /// + /// if is a + /// valid state, otherwise + /// public bool ChangeState(T nextState, out T finalState) { var status = ChangeState(nextState, out Stack states); @@ -30,6 +47,15 @@ public abstract partial class StateMachine : Node where T : Node, IState return status; } + /// + /// Changes the state of the StateMachine. + /// + /// The next state to transition to. + /// Stack of all states that transitioned/proxied. + /// + /// if is a + /// valid state, otherwise + /// public bool ChangeState(T nextState, out Stack states) { states = new Stack(); @@ -71,14 +97,35 @@ public abstract partial class StateMachine : Node where T : Node, IState } /// - /// Changes the current state to a state of type U which must inherit from T. + /// Changes the state of the StateMachine of type + /// which must inherit from + /// . /// + /// The type of the state to transition to. + /// The resulting state to be transitioned to. + /// + /// if is a + /// valid state, otherwise + /// public bool ChangeState(out U state) where U : T { state = this.FindChildOfType(); return ChangeState(state); } + /// + /// Changes the state of the StateMachine with node name + /// . + /// + /// The type of the state to transition to. + /// + /// The name of the node. + /// + /// The resulting state to be transitioned to. + /// + /// if is a + /// valid state, otherwise + /// public bool ChangeState(string name, out T state) { state = GetNode(name);