Player/Mechanics/PostureMechanic.cs

namespace Opium;

/// <summary>
/// The posture of an actor is essentially a second stamina bar, but for block attacks. If you have no posture left, a block will be useless and you'll be hit.
/// </summary>
public partial class PostureMechanic : PlayerMechanic
{
	public float Value { get; set; } = 100f;
	[Property] public float Default { get; set; } = 100f;

	protected TimeSince TimeSinceUsed { get; set; }
	protected TimeUntil TimeUntilRegenerate { get; set; }

	[Property] public float RegenerateTime { get; set; } = 5f;
	[Property] public float RegenerateSpeed { get; set; } = 10f;

	public override bool ShouldBecomeActive() => true;

	protected override void OnStart()
	{
		base.OnStart();

		Value = Default;
	}

	public void Remove( float used, float timeUntilNextRegenerate = 0.0f )
	{
		Value -= used;
		Value = Value.Clamp( 0, 100 );
		TimeSinceUsed = 0;

		if ( timeUntilNextRegenerate != 0f )
		{
			TimeUntilRegenerate = timeUntilNextRegenerate;
		}
		else
		{
			TimeUntilRegenerate = RegenerateTime;
		}
	}

	public void Add( float added )
	{
		Value += added;
		Value = Value.Clamp( 0, 100 );
	}

	public override void OnActiveUpdate()
	{
		if ( TimeUntilRegenerate )
		{
			Add( Time.Delta * RegenerateSpeed );
		}
	}

	public override IEnumerable<string> GetTags()
	{
		if ( Value <= 0 ) yield return "no-posture";
	}

	internal float CalculateDamage( Sandbox.DamageInfo damage )
	{
		if ( damage is Opium.DamageInfo opDamage )
		{
			var wpn = opDamage.Inflictor.Components.Get<MeleeWeapon>();
			if ( wpn is not null )
			{
				return wpn.Posture;
			}
		}
		return 25f;
	}

	/// <summary>
	/// Called when someone is blocking an attack, we will control how much posture we lose here, and react to it if needed.
	/// </summary>
	internal bool Compensate( Sandbox.DamageInfo damage )
	{
		var amount = CalculateDamage( damage );
		// Remove posture based on the damage we receive.
		Remove( amount );

		Log.Info( $"new posture: {Value}, compensating for {amount} posture damage" );

		if ( Value <= 0 )
		{
			// TODO: break posture, animations, events, all that cool stuff.
			Actor.TriggerEvent( "posture_break", damage );
			return true;
		}

		return false;
	}
}