Player/Items/PickupDef.cs

Asset definition for pickup items. Defines display data (name, icon, prefab, effects, sound), drop weight parameters, bot-usage heuristics, and helper methods to get weight by race position and decide if a bot should use the item. Also exposes a static list of all PickupDef resources.

File Access
namespace Machines.Items;

public readonly struct BotItemContext
{
	public float Throttle { get; init; }
	public bool HasFrontCar { get; init; }
	public float FrontDistance { get; init; }
}

/// <summary>
/// Asset type describing a pickup item
/// </summary>
[AssetType( Name = "Pickup", Extension = "pickup" )]
public sealed class PickupDef : GameResource
{
	/// <summary>
	/// The item's display name
	/// </summary>
	[Property]
	public string DisplayName { get; set; } = "";

	/// <summary>
	/// The item's icon
	/// </summary>
	[Property]
	public Texture Icon { get; set; }

	/// <summary>
	/// The item's prefab
	/// </summary>
	[Property]
	public GameObject Prefab { get; set; }

	/// <summary>
	/// One-shot FX spawned on the user when the item is activated.
	/// </summary>
	[Property]
	public GameObject ActivateEffect { get; set; }

	/// <summary>
	/// Sound played when the item is activated.
	/// </summary>
	[Property]
	public SoundEvent ActivateSound { get; set; }

	/// <summary>
	/// Drop weight for a car in 1st place (blended toward <see cref="WeightLast"/> by position).
	/// </summary>
	[Property, Group( "Distribution" )]
	public float WeightFirst { get; set; } = 1f;

	/// <summary>
	/// Drop weight for a car in last place.
	/// </summary>
	[Property, Group( "Distribution" )]
	public float WeightLast { get; set; } = 1f;

	/// <summary>
	/// Bot heuristic: use the item whenever accelerating (good for turbo).
	/// </summary>
	[Property, Group( "Bots" )]
	public bool BotUseOnThrottle { get; set; }

	/// <summary>
	/// Bot heuristic: use when a car ahead is within <see cref="BotUseRange"/>.
	/// </summary>
	[Property, Group( "Bots" )]
	public bool BotUseWhenCarAhead { get; set; }

	/// <summary>
	/// Range for <see cref="BotUseWhenCarAhead"/>.
	/// </summary>
	[Property, Group( "Bots" )]
	public float BotUseRange { get; set; } = 1200f;

	/// <summary>
	/// Returns the drop weight for the given position fraction (0 = leader, 1 = last).
	/// </summary>
	public float WeightAt( float positionFraction )
	{
		return MathX.Lerp( WeightFirst, WeightLast, positionFraction.Clamp( 0f, 1f ) );
	}

	/// <summary>
	/// Whether a bot holding this item should use it right now.
	/// </summary>
	public bool ShouldBotUse( in BotItemContext ctx )
	{
		if ( BotUseOnThrottle )
			return ctx.Throttle > 0.5f;

		if ( BotUseWhenCarAhead )
			return ctx.HasFrontCar && ctx.FrontDistance < BotUseRange;

		// No heuristic set, always use it.
		return true;
	}

	public static IReadOnlyList<PickupDef> All => ResourceLibrary.GetAll<PickupDef>().ToList();
}