Game/Marks.cs
namespace Monolith;

public sealed class MarkDef
{
	public string Id;
	public string Name;
	public string Hint;

	/// <summary>Has this been earned, given the player's current state?</summary>
	public Func<PlayerProgress, bool> Earned;

	/// <summary>Progress toward it, 0 to 1, for the UI. Optional.</summary>
	public Func<PlayerProgress, float> Progress;
}

/// <summary>
/// Achievements, and the quiet global multiplier they carry.
///
/// The multiplier is the smaller half of what these are for. Their real job is to be a **quest
/// log wearing a different hat**: a player who has run out of ideas reads the list and finds
/// six things they had not thought to try. That is what Cookie Clicker's achievements actually
/// do, and why several of these deliberately ask for something awkward rather than for a bigger
/// number you were going to reach anyway.
/// </summary>
public static class Marks
{
	public static readonly MarkDef[] All =
	{
		new()
		{
			Id = "first-cube",
			Name = "First Cut",
			Hint = "Remove a cube.",
			Earned = p => p.Data.LifetimeCubes >= 1,
			Progress = p => (float)Math.Clamp( p.Data.LifetimeCubes, 0, 1 ),
		},
		new()
		{
			Id = "excavator",
			Name = "Excavator",
			Hint = "Remove 100,000 cubes.",
			Earned = p => p.Data.LifetimeCubes >= 100_000,
			Progress = p => (float)(p.Data.LifetimeCubes / 100_000.0),
		},
		new()
		{
			Id = "strip-miner",
			Name = "Strip Miner",
			Hint = "Remove 10 million cubes.",
			Earned = p => p.Data.LifetimeCubes >= 10_000_000,
			Progress = p => (float)(p.Data.LifetimeCubes / 10_000_000.0),
		},
		new()
		{
			Id = "half-way",
			Name = "Halfway Down",
			Hint = "Reach stage 50.",
			Earned = p => p.Data.BestStageEver >= 50,
			Progress = p => p.Data.BestStageEver / 50f,
		},
		new()
		{
			Id = "centurion",
			Name = "Centurion",
			Hint = "Reach stage 100.",
			Earned = p => p.Data.BestStageEver >= 100,
			Progress = p => p.Data.BestStageEver / 100f,
		},
		new()
		{
			Id = "first-collapse",
			Name = "Collapse",
			Hint = "Prestige once.",
			Earned = p => p.Data.Collapses >= 1,
			Progress = p => p.Data.Collapses,
		},
		new()
		{
			Id = "ascendant",
			Name = "Ascendant",
			Hint = "Prestige five times.",
			Earned = p => p.Data.Collapses >= 5,
			Progress = p => p.Data.Collapses / 5f,
		},
		new()
		{
			Id = "buried",
			Name = "Buried Deep",
			Hint = "Detonate 25 charges at full depth.",
			Earned = p => p.Data.DeepDetonations >= 25,
			Progress = p => p.Data.DeepDetonations / 25f,
		},
		new()
		{
			Id = "perfect-hand",
			Name = "Perfect Hand",
			Hint = "Catch 20 active reloads.",
			Earned = p => p.Data.PerfectReloads >= 20,
			Progress = p => p.Data.PerfectReloads / 20f,
		},
		new()
		{
			Id = "unstable",
			Name = "Unstable",
			Hint = "Set off 100 volatile cubes.",
			Earned = p => p.Data.VolatileDetonations >= 100,
			Progress = p => p.Data.VolatileDetonations / 100f,
		},
		new()
		{
			Id = "air-superiority",
			Name = "Air Superiority",
			Hint = "Destroy 50 interceptors.",
			Earned = p => p.Data.InterceptorKills >= 50,
			Progress = p => p.Data.InterceptorKills / 50f,
		},
		new()
		{
			Id = "unseen",
			Name = "Unseen",
			Hint = "Shoot down 10 Spotters.",
			Earned = p => p.Data.SpottersDowned >= 10,
			Progress = p => p.Data.SpottersDowned / 10f,
		},
		new()
		{
			Id = "resonant",
			Name = "Resonant",
			Hint = "Reach 10 Resonance stacks at once.",
			Earned = p => p.Data.BestResonance >= 10,
			Progress = p => p.Data.BestResonance / 10f,
		},
		new()
		{
			Id = "swift",
			Name = "Swift",
			Hint = "Finish the ladder in under 20 minutes.",
			Earned = p => p.Data.BestLadderSeconds > 0f && p.Data.BestLadderSeconds < 1200f,
			Progress = p => p.Data.BestLadderSeconds <= 0f ? 0f : 1200f / p.Data.BestLadderSeconds,
		},
		new()
		{
			Id = "contributor",
			Name = "Contributor",
			Hint = "Earn prestige credit from felling the Monolith.",
			Earned = p => p.Data.MonolithCredits >= 1,
			Progress = p => p.Data.MonolithCredits,
		},
	};

	public static MarkDef Get( string id ) => All.FirstOrDefault( m => m.Id == id );
}