2022-11-18 13:53:51 -08:00
|
|
|
using Godot;
|
2023-06-13 02:55:30 -07:00
|
|
|
using GodotUtilities;
|
2022-11-18 13:53:51 -08:00
|
|
|
using SupaLidlGame.BoundingBoxes;
|
2022-11-13 19:52:09 -08:00
|
|
|
using SupaLidlGame.Characters;
|
2022-11-25 09:11:46 -08:00
|
|
|
using SupaLidlGame.Extensions;
|
2023-05-26 17:42:50 -07:00
|
|
|
using SupaLidlGame.State.Weapon;
|
2022-11-13 19:52:09 -08:00
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
namespace SupaLidlGame.Items.Weapons;
|
|
|
|
|
2023-08-31 19:03:16 -07:00
|
|
|
/// <summary>
|
|
|
|
/// A basic melee weapon.
|
|
|
|
/// </summary>
|
2023-06-03 18:21:46 -07:00
|
|
|
public partial class Sword : Weapon, IParryable
|
2022-11-13 19:52:09 -08:00
|
|
|
{
|
2023-06-03 18:21:46 -07:00
|
|
|
public bool IsAttacking { get; protected set; }
|
2023-03-19 14:13:39 -07:00
|
|
|
|
2023-08-08 00:54:00 -07:00
|
|
|
public override bool IsUsing => IsUsingPrimary || IsUsingAlt;
|
|
|
|
|
|
|
|
public override bool IsUsingPrimary => StateMachine.CurrentState is
|
|
|
|
SwordAttackState;
|
|
|
|
|
|
|
|
public override bool IsUsingAlt => StateMachine.CurrentState is
|
|
|
|
SwordBlockState;
|
2023-05-23 00:23:53 -07:00
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
[Export]
|
|
|
|
public Hitbox Hitbox { get; set; }
|
2023-03-19 14:13:39 -07:00
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
[Export]
|
|
|
|
public AnimationPlayer AnimationPlayer { get; set; }
|
2023-03-19 14:13:39 -07:00
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
/// <summary>
|
|
|
|
/// The time frame in seconds for which the weapon will deal damage.
|
|
|
|
/// </summary>
|
|
|
|
/// <remarks>
|
|
|
|
/// The value of <c>AttackTime</c> should be less than the
|
|
|
|
/// value of <c>UseTime</c>
|
|
|
|
/// </remarks>
|
|
|
|
[Export]
|
|
|
|
public double AttackTime { get; set; } = 0;
|
2023-03-19 14:13:39 -07:00
|
|
|
|
2023-08-08 00:54:00 -07:00
|
|
|
[Export]
|
|
|
|
public double AttackAltTime { get; set; } = 0;
|
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
[Export]
|
|
|
|
public double AttackAnimationDuration { get; set; }
|
2023-03-19 14:13:39 -07:00
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
[Export]
|
2023-07-23 23:39:20 -07:00
|
|
|
public GpuParticles2D ParryParticles { get; set; }
|
2023-05-15 00:20:17 -07:00
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
[Export]
|
|
|
|
public double NPCAnticipateTime { get; set; }
|
2023-05-15 00:20:17 -07:00
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
[Export]
|
|
|
|
public WeaponStateMachine StateMachine { get; set; }
|
2023-05-15 00:20:17 -07:00
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
[Export]
|
|
|
|
public Node2D Anchor { get; set; }
|
2023-05-26 22:28:08 -07:00
|
|
|
|
2024-03-25 20:54:56 -07:00
|
|
|
public bool HasParried { get; protected set; }
|
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
public override bool IsParryable { get; protected set; }
|
2023-05-15 00:20:17 -07:00
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
public ulong ParryTimeOrigin { get; protected set; }
|
2023-05-15 00:20:17 -07:00
|
|
|
|
2024-03-29 16:17:19 -07:00
|
|
|
[Export]
|
|
|
|
public float BlockForce { get; set; } = 1;
|
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
private Tween _currentTween;
|
2023-05-15 00:20:17 -07:00
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
private AnimationNodeStateMachinePlayback _playback;
|
2023-05-15 00:20:17 -07:00
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
public override void Equip(Character character)
|
|
|
|
{
|
|
|
|
base.Equip(character);
|
|
|
|
Hitbox.Faction = character.Faction; // character is null before base
|
|
|
|
}
|
2023-03-19 14:13:39 -07:00
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
public override void Unequip(Character character)
|
|
|
|
{
|
|
|
|
base.Unequip(character);
|
|
|
|
}
|
|
|
|
|
2023-08-08 00:54:00 -07:00
|
|
|
public void EnableParry()
|
2023-06-03 18:21:46 -07:00
|
|
|
{
|
2023-08-08 00:54:00 -07:00
|
|
|
EnableParry(Time.GetTicksMsec());
|
2023-06-03 18:21:46 -07:00
|
|
|
}
|
2023-03-19 14:13:39 -07:00
|
|
|
|
2023-08-31 19:03:16 -07:00
|
|
|
/// <summary>
|
|
|
|
/// Makes this melee weapon be able to parry and be parried.
|
|
|
|
/// </summary>
|
2023-08-08 00:54:00 -07:00
|
|
|
public void EnableParry(ulong parryTimeOrigin)
|
2023-06-03 18:21:46 -07:00
|
|
|
{
|
2024-03-25 20:54:56 -07:00
|
|
|
HasParried = false;
|
2023-06-03 18:21:46 -07:00
|
|
|
IsParried = false;
|
|
|
|
IsParryable = true;
|
2023-08-08 00:54:00 -07:00
|
|
|
ParryTimeOrigin = parryTimeOrigin;
|
2023-06-03 18:21:46 -07:00
|
|
|
}
|
2023-03-19 14:13:39 -07:00
|
|
|
|
2023-08-31 19:03:16 -07:00
|
|
|
/// <summary>
|
|
|
|
/// Makes this melee weapon be able to parry and be parried.
|
|
|
|
/// </summary>
|
2023-06-03 18:21:46 -07:00
|
|
|
public void DisableParry()
|
|
|
|
{
|
2024-03-25 20:54:56 -07:00
|
|
|
HasParried = false;
|
|
|
|
IsParried = false;
|
2023-06-03 18:21:46 -07:00
|
|
|
IsParryable = false;
|
|
|
|
}
|
2023-05-23 00:23:53 -07:00
|
|
|
|
2023-08-08 00:54:00 -07:00
|
|
|
public override void Use()
|
|
|
|
{
|
|
|
|
StateMachine.Use();
|
|
|
|
base.Use();
|
|
|
|
}
|
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
public override void Deuse()
|
|
|
|
{
|
2023-07-23 23:39:20 -07:00
|
|
|
StateMachine.Deuse();
|
2023-06-03 18:21:46 -07:00
|
|
|
base.Deuse();
|
|
|
|
}
|
2023-05-23 00:23:53 -07:00
|
|
|
|
2023-08-08 00:54:00 -07:00
|
|
|
public override void UseAlt()
|
|
|
|
{
|
|
|
|
StateMachine.UseAlt();
|
|
|
|
base.UseAlt();
|
|
|
|
}
|
|
|
|
|
|
|
|
public override void DeuseAlt()
|
|
|
|
{
|
|
|
|
StateMachine.DeuseAlt();
|
|
|
|
base.DeuseAlt();
|
|
|
|
}
|
|
|
|
|
2023-08-31 19:03:16 -07:00
|
|
|
/// <summary>
|
|
|
|
/// Enables the weapon's hitbox. Prefer to call this from a state machine
|
|
|
|
/// rather than managing state through the weapon script.
|
|
|
|
/// </summary>
|
2023-06-03 18:21:46 -07:00
|
|
|
public void Attack()
|
|
|
|
{
|
|
|
|
//RemainingAttackTime = AttackTime;
|
|
|
|
IsAttacking = true;
|
|
|
|
Hitbox.IsDisabled = false;
|
|
|
|
}
|
2023-03-19 14:13:39 -07:00
|
|
|
|
2023-08-31 19:03:16 -07:00
|
|
|
/// <summary>
|
|
|
|
/// Disables the weapon's hitbox and processes all hurtboxes it hit.
|
|
|
|
/// </summary>
|
2023-06-03 18:21:46 -07:00
|
|
|
public void Deattack()
|
|
|
|
{
|
|
|
|
IsAttacking = false;
|
|
|
|
Hitbox.IsDisabled = true;
|
|
|
|
ProcessHits();
|
2024-03-25 20:54:56 -07:00
|
|
|
DisableParry();
|
2023-06-03 18:21:46 -07:00
|
|
|
Hitbox.ResetIgnoreList();
|
|
|
|
AnimationPlayer.SpeedScale = 1;
|
|
|
|
}
|
2023-03-19 14:13:39 -07:00
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
public override void _Ready()
|
|
|
|
{
|
|
|
|
Hitbox.Damage = Damage;
|
2023-07-23 11:03:55 -07:00
|
|
|
Hitbox.Hit += OnHitboxHit;
|
2023-08-10 23:08:41 -07:00
|
|
|
StateMachine.StateChanged += OnStateChanged;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void OnStateChanged(Node state)
|
|
|
|
{
|
|
|
|
if (StateMachine.UsedItemStates is null)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (var nodePath in StateMachine.UsedItemStates)
|
|
|
|
{
|
|
|
|
if (StateMachine.GetNode(nodePath) == state)
|
|
|
|
{
|
|
|
|
Character.Inventory.EmitSignal(
|
|
|
|
Inventory.SignalName.UsedItem, this);
|
|
|
|
//EmitSignal(SignalName.UsedItem, this);
|
|
|
|
}
|
|
|
|
}
|
2023-06-03 18:21:46 -07:00
|
|
|
}
|
2023-03-19 14:13:39 -07:00
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
public override void _Process(double delta)
|
|
|
|
{
|
|
|
|
StateMachine.Process(delta);
|
|
|
|
base._Process(delta);
|
|
|
|
}
|
2023-03-19 14:13:39 -07:00
|
|
|
|
2023-08-31 19:03:16 -07:00
|
|
|
/// <summary>
|
|
|
|
/// Processes all hits and applies damages to hurtboxes.
|
|
|
|
/// </summary>
|
2023-06-03 18:21:46 -07:00
|
|
|
public void ProcessHits()
|
|
|
|
{
|
2024-03-25 20:54:56 -07:00
|
|
|
if (IsParried || HasParried)
|
2023-03-19 14:13:39 -07:00
|
|
|
{
|
2023-06-03 18:21:46 -07:00
|
|
|
return;
|
2023-03-19 14:13:39 -07:00
|
|
|
}
|
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
foreach (BoundingBox box in Hitbox.Hits)
|
2023-03-19 14:13:39 -07:00
|
|
|
{
|
2023-06-03 18:21:46 -07:00
|
|
|
if (box is Hurtbox hurtbox)
|
2023-03-19 14:13:39 -07:00
|
|
|
{
|
2023-09-03 17:42:32 -07:00
|
|
|
hurtbox.InflictDamage(
|
|
|
|
Damage,
|
|
|
|
Character,
|
|
|
|
Knockback,
|
|
|
|
this);
|
2023-03-19 14:13:39 -07:00
|
|
|
}
|
|
|
|
}
|
2024-09-16 12:22:30 -07:00
|
|
|
|
|
|
|
if (Hitbox.Hits.Count > 0)
|
|
|
|
{
|
|
|
|
Character.ApplyImpulse(-Character.Target.Normalized() * Knockback);
|
|
|
|
}
|
2023-06-03 18:21:46 -07:00
|
|
|
}
|
2023-03-19 14:13:39 -07:00
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
public void AttemptParry(Weapon otherWeapon)
|
|
|
|
{
|
|
|
|
if (otherWeapon.IsParryable &&
|
2023-07-23 23:39:20 -07:00
|
|
|
otherWeapon is IParryable otherParryable)
|
2023-03-19 14:13:39 -07:00
|
|
|
{
|
2023-06-03 18:21:46 -07:00
|
|
|
if (ParryTimeOrigin < otherParryable.ParryTimeOrigin)
|
2023-03-19 14:13:39 -07:00
|
|
|
{
|
2023-06-03 18:21:46 -07:00
|
|
|
// our character was parried
|
2023-07-23 23:39:20 -07:00
|
|
|
ParryParticles.CloneOnWorld<GpuParticles2D>().EmitOneShot();
|
2024-03-29 16:17:19 -07:00
|
|
|
Stun(otherParryable.BlockForce);
|
2023-08-08 00:54:00 -07:00
|
|
|
|
|
|
|
if (otherParryable is Sword sword)
|
|
|
|
{
|
|
|
|
if (sword.StateMachine.CurrentState is SwordBlockState b)
|
|
|
|
{
|
|
|
|
b.HasBlocked = true;
|
|
|
|
}
|
|
|
|
}
|
2023-03-19 14:13:39 -07:00
|
|
|
}
|
2024-03-25 20:54:56 -07:00
|
|
|
else
|
|
|
|
{
|
|
|
|
HasParried = true;
|
|
|
|
}
|
2023-03-19 14:13:39 -07:00
|
|
|
}
|
2023-06-03 18:21:46 -07:00
|
|
|
}
|
|
|
|
|
2023-08-31 19:03:16 -07:00
|
|
|
/// <summary>
|
|
|
|
/// Stuns the wepaon holder. This is unique to swords and melee weapons
|
|
|
|
/// if they can parry.
|
|
|
|
/// </summary>
|
2024-03-29 16:17:19 -07:00
|
|
|
public void Stun(float blockForce)
|
2023-06-03 18:21:46 -07:00
|
|
|
{
|
|
|
|
IsParried = true;
|
2024-03-29 16:17:19 -07:00
|
|
|
Character.Stats.AddStaggerDamage(Damage * blockForce);
|
|
|
|
|
|
|
|
// optionally play parry sound
|
|
|
|
GetNode<AudioStreamPlayer2D>("ParrySound")?
|
2023-07-23 23:39:20 -07:00
|
|
|
.OnWorld()
|
|
|
|
.WithPitchDeviation(0.125f)
|
|
|
|
.PlayOneShot();
|
2023-06-03 18:21:46 -07:00
|
|
|
}
|
2023-03-19 14:13:39 -07:00
|
|
|
|
2023-07-23 11:03:55 -07:00
|
|
|
public override void OnHitboxHit(BoundingBox box)
|
2023-06-03 18:21:46 -07:00
|
|
|
{
|
|
|
|
if (IsParried)
|
2023-05-23 00:23:53 -07:00
|
|
|
{
|
2023-06-03 18:21:46 -07:00
|
|
|
return;
|
2023-05-23 00:23:53 -07:00
|
|
|
}
|
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
if (box is Hitbox hb)
|
2023-03-19 14:13:39 -07:00
|
|
|
{
|
2023-06-03 18:21:46 -07:00
|
|
|
Weapon w = hb.GetAncestor<Weapon>();
|
|
|
|
if (w is not null)
|
2023-03-19 14:13:39 -07:00
|
|
|
{
|
2023-06-03 18:21:46 -07:00
|
|
|
AttemptParry(w);
|
2023-03-19 14:13:39 -07:00
|
|
|
}
|
2023-06-03 18:21:46 -07:00
|
|
|
}
|
2023-03-19 14:13:39 -07:00
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
if (box is Hurtbox hurt)
|
|
|
|
{
|
|
|
|
if (hurt.GetParent() is Character c)
|
2023-03-19 14:13:39 -07:00
|
|
|
{
|
2023-06-03 18:21:46 -07:00
|
|
|
var item = c.Inventory.SelectedItem;
|
|
|
|
if (item is Weapon w)
|
2023-03-19 14:13:39 -07:00
|
|
|
{
|
|
|
|
AttemptParry(w);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-06-03 18:21:46 -07:00
|
|
|
}
|
2022-11-13 19:52:09 -08:00
|
|
|
}
|