Files
Emberwild/Assets/GAME/Script/Player/HeldItems/BuildHammerBehaviour.cs
T
2026-07-09 21:38:14 +02:00

169 lines
5.7 KiB
C#

using UnityEngine;
using Ashwild.Inventory;
namespace Ashwild.Player
{
/// <summary>
/// Held-item logic for the build hammer, placed on its hand prefab. It turns the single
/// right-click into two gestures without any extra keybind: a quick <b>tap</b> opens/closes the
/// construction menu through the bus, while <b>holding</b> right-click past a short threshold puts
/// the hammer into demolition mode for as long as it is held (releasing leaves it). The hammer
/// stays a pure input source — it only raises bus requests; BuildManager owns the menu and the
/// world-facing targeting/destroy, so this never reaches for either.
/// </summary>
[DisallowMultipleComponent]
public class BuildHammerBehaviour : MonoBehaviour, IHeldItemBehaviour
{
#region Serialized Fields
[Header("Gestures")]
/// <summary>
/// How long, in seconds, right-click must stay held before it counts as a demolition hold
/// instead of a menu tap. A release before this opens/closes the menu.
/// </summary>
[SerializeField] private float holdThreshold = 0.2f;
/// <summary>
/// Minimum time, in seconds, between two menu toggles so a burst of taps does not flicker the
/// menu open and shut.
/// </summary>
[SerializeField] private float toggleCooldown = 0.25f;
#endregion
#region State
/// <summary>
/// Earliest time, in seconds, the next menu toggle is allowed.
/// </summary>
private float nextToggleTime;
/// <summary>
/// True between an accepted press and its release. A press ignored at the gate (input locked
/// or already placing) never sets this, so its release is ignored too.
/// </summary>
private bool secondaryDown;
/// <summary>
/// Time, in seconds, the current accepted press began — measured against <see cref="holdThreshold"/>.
/// </summary>
private float secondaryDownTime;
/// <summary>
/// True while the hold has crossed into demolition mode, so the release ends the mode rather
/// than toggling the menu.
/// </summary>
private bool demoActive;
#endregion
#region Unity Lifecycle
/// <summary>
/// Subscribes to the right-click hold stream for as long as the hammer is held.
/// </summary>
private void OnEnable()
{
PlayerEvents.SecondaryUseHeld += HandleSecondaryUse;
}
/// <summary>
/// Unsubscribes and leaves demolition mode cleanly when the hammer is put away (its prefab is
/// destroyed mid-hold), so the mode never sticks on with no hammer to release it.
/// </summary>
private void OnDisable()
{
PlayerEvents.SecondaryUseHeld -= HandleSecondaryUse;
if (demoActive) ExitDemolition();
secondaryDown = false;
}
/// <summary>
/// Promotes a sustained press into demolition mode once it outlasts the tap threshold. Held
/// out of Update (not a timer) so the mode drops instantly if input locks or a placement
/// begins mid-hold.
/// </summary>
private void Update()
{
if (!secondaryDown || demoActive) return;
if (PlayerEvents.InputLocked || PlayerEvents.IsPlacingBuild) return;
if (Time.time - secondaryDownTime < holdThreshold) return;
EnterDemolition();
}
#endregion
#region IHeldItemBehaviour
/// <summary>
/// Nothing to link: the hammer only raises bus requests. Present to satisfy the held-item
/// contract; BuildManager holds the player/world refs the build UX needs.
/// </summary>
public void Setup(HeldItemContext context, ItemData item)
{
}
#endregion
#region Event Handlers
/// <summary>
/// The single right-click gesture, split by press and release: a press is tracked (unless
/// input is locked or a ghost is being placed, where right-click means "cancel" and is left to
/// BuildManager); its release either ends demolition mode (if the hold engaged it) or, for a
/// quick tap, toggles the construction menu under the cooldown.
/// </summary>
private void HandleSecondaryUse(bool held)
{
if (held)
{
if (PlayerEvents.InputLocked || PlayerEvents.IsPlacingBuild) return;
secondaryDown = true;
secondaryDownTime = Time.time;
return;
}
if (!secondaryDown) return;
secondaryDown = false;
if (demoActive) ExitDemolition();
else ToggleMenu();
}
#endregion
#region Internal Helpers
/// <summary>
/// Requests the construction menu to open/close, gated by the toggle cooldown.
/// </summary>
private void ToggleMenu()
{
if (Time.time < nextToggleTime) return;
nextToggleTime = Time.time + toggleCooldown;
PlayerEvents.RaiseBuildMenuToggleRequested();
}
/// <summary>
/// Enters demolition mode and announces it on the bus.
/// </summary>
private void EnterDemolition()
{
demoActive = true;
PlayerEvents.RaiseDemolishModeChanged(true);
}
/// <summary>
/// Leaves demolition mode and announces it on the bus.
/// </summary>
private void ExitDemolition()
{
demoActive = false;
PlayerEvents.RaiseDemolishModeChanged(false);
}
#endregion
}
}