FoodDef.cs

Data class defining food types for the game. Declares properties for stats (boosts, costs, pellet visuals), several predefined FoodDef instances (Flakes, Pellets, FreezeDried, etc.), a Catalog list, and lookup helpers Find and FindExact.

namespace NoChillquarium;

/// <summary>
/// Escalating food ladder. Nothing free — buy Fish Flakes first, then weirder food.
/// </summary>
public sealed class FoodDef
{
	public string Id { get; init; }
	public string Name { get; init; }
	/// <summary>Optional short HUD label (inventory). Falls back to <see cref="Name"/>.</summary>
	public string InvName { get; init; }
	public string Note { get; init; }
	/// <summary>Name for inventory / compact UI.</summary>
	public string DisplayInv => string.IsNullOrWhiteSpace( InvName ) ? Name : InvName;
	public double UnlockCost { get; init; }
	public float BoostDuration { get; init; }
	public float BoostMultiplier { get; init; }
	public float ChaosOnFeed { get; init; }
	public int PelletCount { get; init; }
	public string PelletColor { get; init; }
	public float BobMulWhileBoost { get; init; }
	public float SpeedMulWhileBoost { get; init; }
	/// <summary>Playtime seconds required before unlock is available (0 = start).</summary>
	public float UnlockPlaytime { get; init; }

	public static readonly FoodDef Flakes = new()
	{
		Id = "flakes",
		Name = "Fish Flakes",
		InvName = "Fish Flakes",
		Note = "starter",
		UnlockCost = 10,
		BoostDuration = 6f,
		BoostMultiplier = 1.75f,
		ChaosOnFeed = 0f,
		PelletCount = 5,
		PelletColor = "#e8c060",
		BobMulWhileBoost = 1f,
		SpeedMulWhileBoost = 1f,
		UnlockPlaytime = 0f
	};

	public static readonly FoodDef Pellets = new()
	{
		Id = "pellets",
		Name = "Pellets",
		Note = "crunch",
		UnlockCost = 20,
		BoostDuration = 8f,
		BoostMultiplier = 2.0f,
		ChaosOnFeed = 0f,
		PelletCount = 6,
		PelletColor = "#d09040",
		BobMulWhileBoost = 1.05f,
		SpeedMulWhileBoost = 1.05f,
		UnlockPlaytime = 0f
	};

	public static readonly FoodDef FreezeDried = new()
	{
		Id = "freeze_dried",
		Name = "Freeze-Dried",
		Note = "fancy",
		UnlockCost = 45,
		BoostDuration = 9f,
		BoostMultiplier = 2.25f,
		ChaosOnFeed = 0.5f,
		PelletCount = 7,
		PelletColor = "#c07050",
		BobMulWhileBoost = 1.1f,
		SpeedMulWhileBoost = 1.1f,
		UnlockPlaytime = 20f * 60f // ~20 min
	};

	public static readonly FoodDef FriedChicken = new()
	{
		Id = "fried_chicken",
		Name = "Fried Chicken",
		Note = "greasy",
		UnlockCost = 80,
		BoostDuration = 10f,
		BoostMultiplier = 2.6f,
		ChaosOnFeed = 2f,
		PelletCount = 5,
		PelletColor = "#e8a040",
		BobMulWhileBoost = 0.85f,
		SpeedMulWhileBoost = 0.9f,
		UnlockPlaytime = 40f * 60f // ~40 min
	};

	public static readonly FoodDef WeedGummies = new()
	{
		Id = "weed_gummies",
		Name = "Weed Gummies",
		InvName = "Gummies",
		Note = "trippy",
		UnlockCost = 110,
		BoostDuration = 14f,
		BoostMultiplier = 2.4f,
		ChaosOnFeed = 4f,
		PelletCount = 6,
		PelletColor = "#50d060",
		BobMulWhileBoost = 0.5f,
		SpeedMulWhileBoost = 0.55f,
		UnlockPlaytime = 60f * 60f // ~1h
	};

	public static readonly FoodDef OtherFish = new()
	{
		Id = "other_fish",
		Name = "Other Fish",
		Note = "cannibal",
		UnlockCost = 160,
		BoostDuration = 11f,
		BoostMultiplier = 3.0f,
		ChaosOnFeed = 6f,
		PelletCount = 4,
		PelletColor = "#e06070",
		BobMulWhileBoost = 1.2f,
		SpeedMulWhileBoost = 1.15f,
		UnlockPlaytime = 90f * 60f // ~1h30
	};

	public static readonly FoodDef Hand = new()
	{
		Id = "hand",
		Name = "Dismembered Hand",
		Note = "hand",
		UnlockCost = 220,
		BoostDuration = 12f,
		BoostMultiplier = 3.4f,
		ChaosOnFeed = 10f,
		PelletCount = 3,
		PelletColor = "#d08070",
		BobMulWhileBoost = 1.3f,
		SpeedMulWhileBoost = 1.2f,
		UnlockPlaytime = 2.25f * 3600f // ~2h15
	};

	public static readonly FoodDef HomelessMan = new()
	{
		Id = "homeless_man",
		Name = "Homeless Man",
		Note = "unhinged",
		UnlockCost = 350,
		BoostDuration = 14f,
		BoostMultiplier = 4.0f,
		ChaosOnFeed = 15f,
		PelletCount = 4,
		PelletColor = "#a09080",
		BobMulWhileBoost = 1.4f,
		SpeedMulWhileBoost = 1.25f,
		UnlockPlaytime = 3f * 3600f // ~3h
	};

	public static IReadOnlyList<FoodDef> Catalog { get; } = new[]
	{
		Flakes,
		Pellets,
		FreezeDried,
		FriedChicken,
		WeedGummies,
		OtherFish,
		Hand,
		HomelessMan
	};

	public static FoodDef Find( string id ) =>
		Catalog.FirstOrDefault( f => f.Id == id ) ?? Flakes;

	/// <summary>Lookup without flakes fallback (null if unknown / empty).</summary>
	public static FoodDef FindExact( string id ) =>
		string.IsNullOrWhiteSpace( id ) ? null : Catalog.FirstOrDefault( f => f.Id == id );
}