Vendor.cs
using Sandbox;
using SWB.Shared;
using System.Collections.Generic;

// One buyable line on the vendor's board. Configure these in the editor -
// ClassName has to match a weapon registered in the WeaponRegistry
// (swb_colt, swb_scarh, swb_remington, swb_veresk, swb_l96a1, ...).
public sealed class VendorEntry
{
	[Property] public string ClassName { get; set; }
	[Property] public int Cost { get; set; } = 500;
	[Property] public LoadoutSlot Slot { get; set; } = LoadoutSlot.Primary;
}

// The Hideout loadout vendor. Put this on a GameObject with a trigger
// Collider covering the counter area; walk in and hold Use to open the
// board, hold Use again (or walk away) to close it.
public sealed class Vendor : Component, Component.ITriggerListener, IInteractable
{
	[Property]
	public List<VendorEntry> Stock { get; set; } = new();

	[Property]
	public float HoldDuration { get; set; } = 0.4f;

	bool _playerInRange = false;
	float _holdProgress = 0f;
	bool _wasHolding = false;

	public bool IsOpen { get; private set; }

	// Drives the on-screen prompt - see IInteractable.
	public bool PlayerInRange => _playerInRange;
	public float HoldFraction => HoldDuration <= 0f ? 0f : _holdProgress / HoldDuration;
	public string PromptText => "Hold E to shop";
	public bool SuppressPrompt => IsOpen;

	public void OnTriggerEnter( Collider other )
	{
		if ( other.GameObject.Root.Tags.Has( "player" ) )
			_playerInRange = true;
	}

	public void OnTriggerExit( Collider other )
	{
		if ( !other.GameObject.Root.Tags.Has( "player" ) )
			return;

		_playerInRange = false;
		_holdProgress = 0f;
		Close();
	}

	protected override void OnUpdate()
	{
		if ( !_playerInRange )
			return;

		var holding = Input.Down( InputButtonHelper.Use );

		if ( holding )
		{
			_holdProgress += Time.Delta;

			// Only toggle on the hold completing, then wait for the
			// button to come back up - otherwise one long press would
			// flap the menu open and shut every HoldDuration seconds.
			if ( _holdProgress >= HoldDuration && !_wasHolding )
			{
				_wasHolding = true;

				if ( IsOpen )
					Close();
				else
					Open();
			}
		}
		else
		{
			_holdProgress = 0f;
			_wasHolding = false;
		}
	}

	void Open()
	{
		IsOpen = true;
	}

	public void Close()
	{
		IsOpen = false;
	}
}