clean up & some documentation
							parent
							
								
									8e7750111b
								
							
						
					
					
						commit
						ce093646aa
					
				|  | @ -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 | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Handles the <c>Character</c>'s death. | ||||
|     /// </summary> | ||||
|     public virtual void Die() | ||||
|     { | ||||
|         if (HurtAnimation.HasAnimation("death")) | ||||
|  | @ -190,11 +190,20 @@ public partial class Character : CharacterBody2D, IFaction | |||
|         NetImpulse += impulse / Mass; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Stuns the <c>Chararacter</c> for an amount of time. If | ||||
|     /// <paramref name="time"/> is less than the <c>Character</c>'s current | ||||
|     /// stun time left, it will have no effect. | ||||
|     /// </summary> | ||||
|     public virtual void Stun(float time) | ||||
|     { | ||||
|         StunTime = Mathf.Max(time, StunTime); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Draws the character so that its sprite and inventory items face the | ||||
|     /// character's direction. | ||||
|     /// </summary> | ||||
|     protected virtual void DrawTarget() | ||||
|     { | ||||
|         Vector2 target = Target; | ||||
|  | @ -213,6 +222,10 @@ public partial class Character : CharacterBody2D, IFaction | |||
|         Inventory.Rotation = angle; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Use the current item the character is using. Prefer to call this over | ||||
|     /// <c>Item.Use</c> as it will check if the character is stunned or alive. | ||||
|     /// </summary> | ||||
|     public void UseCurrentItem() | ||||
|     { | ||||
|         if (StunTime > 0 || !IsAlive) | ||||
|  | @ -264,6 +277,9 @@ public partial class Character : CharacterBody2D, IFaction | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Override this method to modify the damage the character takes. | ||||
|     /// </summary> | ||||
|     protected virtual float ReceiveDamage( | ||||
|         float damage, | ||||
|         Character inflictor, | ||||
|  | @ -285,6 +301,9 @@ public partial class Character : CharacterBody2D, IFaction | |||
|         _curDamageText.ShowText(); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Handles the character taking damage. | ||||
|     /// </summary> | ||||
|     protected virtual void OnReceivedDamage( | ||||
|         float damage, | ||||
|         Character inflictor, | ||||
|  | @ -348,6 +367,7 @@ public partial class Character : CharacterBody2D, IFaction | |||
|         } | ||||
|     } | ||||
| 
 | ||||
| #if DEBUG | ||||
|     /// <summary> | ||||
|     /// For debugging purposes | ||||
|     /// </summary> | ||||
|  | @ -355,7 +375,12 @@ public partial class Character : CharacterBody2D, IFaction | |||
|     { | ||||
|         OnReceivedDamage(damage, null, 0); | ||||
|     } | ||||
| #endif | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Plays a footstep sound. This should be called through an | ||||
|     /// <c>AnimationPlayer</c> to sync sounds with animations. | ||||
|     /// </summary> | ||||
|     public virtual void Footstep() | ||||
|     { | ||||
|         if (GetNode("Effects/Footstep") is AudioStreamPlayer2D player) | ||||
|  | @ -364,6 +389,15 @@ public partial class Character : CharacterBody2D, IFaction | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Returns whether the <c>Character</c> has line of sight with | ||||
|     /// <paramref name="character"/>. | ||||
|     /// </summary> | ||||
|     /// <param name="character">The character to check for LOS</param> | ||||
|     /// <param name="excludeClip"> | ||||
|     /// Determines whether the raycast should pass through world clips (physics | ||||
|     /// layer 5) | ||||
|     /// </param> | ||||
|     public bool HasLineOfSight(Character character, bool excludeClip = false) | ||||
|     { | ||||
|         var exclude = new Godot.Collections.Array<Godot.Rid>(); | ||||
|  |  | |||
|  | @ -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(); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Finds the NPC's best character to target. | ||||
|     /// </summary> | ||||
|     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<Godot.Rid>(); | ||||
|         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(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -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 | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Respawns the player with full health and plays spawn animation | ||||
|     /// </summary> | ||||
|     public void Spawn() | ||||
|     { | ||||
|         Health = 100; | ||||
|  |  | |||
|  | @ -23,6 +23,12 @@ public abstract partial class Item : Node2D | |||
| 
 | ||||
|     public Character CharacterOwner { get; set; } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Determines if the item is being used. This property determines if | ||||
|     /// a character can use another item or not. | ||||
|     /// See <see cref="Character.UseCurrentItem"/> | ||||
|     /// </summary> | ||||
|     ///  | ||||
|     public virtual bool IsUsing => false; | ||||
| 
 | ||||
|     /// <summary> | ||||
|  |  | |||
|  | @ -7,6 +7,9 @@ using SupaLidlGame.State.Weapon; | |||
| 
 | ||||
| namespace SupaLidlGame.Items.Weapons; | ||||
| 
 | ||||
| /// <summary> | ||||
| /// A basic melee weapon. | ||||
| /// </summary> | ||||
| 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()); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Makes this melee weapon be able to parry and be parried. | ||||
|     /// </summary> | ||||
|     public void EnableParry(ulong parryTimeOrigin) | ||||
|     { | ||||
|         IsParried = false; | ||||
|  | @ -84,6 +90,9 @@ public partial class Sword : Weapon, IParryable | |||
|         ParryTimeOrigin = parryTimeOrigin; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Makes this melee weapon be able to parry and be parried. | ||||
|     /// </summary> | ||||
|     public void DisableParry() | ||||
|     { | ||||
|         IsParryable = false; | ||||
|  | @ -113,6 +122,10 @@ public partial class Sword : Weapon, IParryable | |||
|         base.DeuseAlt(); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Enables the weapon's hitbox. Prefer to call this from a state machine | ||||
|     /// rather than managing state through the weapon script. | ||||
|     /// </summary> | ||||
|     public void Attack() | ||||
|     { | ||||
|         //RemainingAttackTime = AttackTime; | ||||
|  | @ -120,6 +133,9 @@ public partial class Sword : Weapon, IParryable | |||
|         Hitbox.IsDisabled = false; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Disables the weapon's hitbox and processes all hurtboxes it hit. | ||||
|     /// </summary> | ||||
|     public void Deattack() | ||||
|     { | ||||
|         IsAttacking = false; | ||||
|  | @ -161,6 +177,9 @@ public partial class Sword : Weapon, IParryable | |||
|         base._Process(delta); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Processes all hits and applies damages to hurtboxes. | ||||
|     /// </summary> | ||||
|     public void ProcessHits() | ||||
|     { | ||||
|         if (IsParried) | ||||
|  | @ -199,6 +218,10 @@ public partial class Sword : Weapon, IParryable | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Stuns the wepaon holder. This is unique to swords and melee weapons | ||||
|     /// if they can parry. | ||||
|     /// </summary> | ||||
|     public void Stun() | ||||
|     { | ||||
|         IsParried = true; | ||||
|  | @ -238,9 +261,4 @@ public partial class Sword : Weapon, IParryable | |||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     protected void SetAnimationCondition(string condition, bool value) | ||||
|     { | ||||
| 
 | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -18,11 +18,28 @@ public abstract partial class StateMachine<T> : Node where T : Node, IState<T> | |||
|         ChangeState(InitialState); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Changes the state of the <c>StateMachine</c>. | ||||
|     /// </summary> | ||||
|     /// <param name="nextState">The next state to transition to.</param> | ||||
|     /// <returns> | ||||
|     /// <see langword="true" /> if <paramref name="nextState" /> is a | ||||
|     /// valid state, otherwise <see langword="false" /> | ||||
|     /// </returns> | ||||
|     public virtual bool ChangeState(T nextState) | ||||
|     { | ||||
|         return ChangeState(nextState, out Stack<T> _); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Changes the state of the <c>StateMachine</c>. | ||||
|     /// </summary> | ||||
|     /// <param name="nextState">The next state to transition to.</param> | ||||
|     /// <param name="finalState">The actual state.</param> | ||||
|     /// <returns> | ||||
|     /// <see langword="true" /> if <paramref name="nextState" /> is a | ||||
|     /// valid state, otherwise <see langword="false" /> | ||||
|     /// </returns> | ||||
|     public bool ChangeState(T nextState, out T finalState) | ||||
|     { | ||||
|         var status = ChangeState(nextState, out Stack<T> states); | ||||
|  | @ -30,6 +47,15 @@ public abstract partial class StateMachine<T> : Node where T : Node, IState<T> | |||
|         return status; | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Changes the state of the <c>StateMachine</c>. | ||||
|     /// </summary> | ||||
|     /// <param name="nextState">The next state to transition to.</param> | ||||
|     /// <param name="states">Stack of all states that transitioned/proxied.</param> | ||||
|     /// <returns> | ||||
|     /// <see langword="true" /> if <paramref name="nextState" /> is a | ||||
|     /// valid state, otherwise <see langword="false" /> | ||||
|     /// </returns> | ||||
|     public bool ChangeState(T nextState, out Stack<T> states) | ||||
|     { | ||||
|         states = new Stack<T>(); | ||||
|  | @ -71,14 +97,35 @@ public abstract partial class StateMachine<T> : Node where T : Node, IState<T> | |||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Changes the current state to a state of type U which must inherit from T. | ||||
|     /// Changes the state of the <c>StateMachine</c> of type | ||||
|     /// <typeparamref name="U" /> which must inherit from | ||||
|     /// <typeparamref name="T" />. | ||||
|     /// </summary> | ||||
|     /// <typeparam name="U">The type of the state to transition to.</typeparam> | ||||
|     /// <param name="state">The resulting state to be transitioned to.</param> | ||||
|     /// <returns> | ||||
|     /// <see langword="true" /> if <paramref name="nextState" /> is a | ||||
|     /// valid state, otherwise <see langword="false" /> | ||||
|     /// </returns> | ||||
|     public bool ChangeState<U>(out U state) where U : T | ||||
|     { | ||||
|         state = this.FindChildOfType<U>(); | ||||
|         return ChangeState(state); | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Changes the state of the <c>StateMachine</c> with node name | ||||
|     /// <paramref name="name" />. | ||||
|     /// </summary> | ||||
|     /// <typeparam name="U">The type of the state to transition to.</typeparam> | ||||
|     /// <param name="name"> | ||||
|     /// The name of the <typeparamref name="T" /> node. | ||||
|     /// </param> | ||||
|     /// <param name="state">The resulting state to be transitioned to.</param> | ||||
|     /// <returns> | ||||
|     /// <see langword="true" /> if <paramref name="nextState" /> is a | ||||
|     /// valid state, otherwise <see langword="false" /> | ||||
|     /// </returns> | ||||
|     public bool ChangeState(string name, out T state) | ||||
|     { | ||||
|         state = GetNode<T>(name); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue