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.
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();
}