GameState.cs
namespace NiuLai;

/// <summary>Film order. The game is the movie, just playable and still crooked.</summary>
public enum Beat
{
	Migrate,
	Dream,
	Snake,
	Creek,
	Night,
	Suspect,
	Wolves,
	Ban,
	Canyon,
	Cart,
	Farewell,
	Wake,
	Reunion,
	Credits
}

public static class GameState
{
	public static Beat Beat { get; private set; } = Beat.Migrate;
	public static int GrassEaten { get; private set; }
	public static int GrassBag { get; private set; }
	public static int Courage { get; private set; }
	public static bool Bitten { get; private set; }
	public static bool CreekJumped { get; private set; }
	public static bool FedLeopard { get; private set; }
	public static bool HerdSuspects { get; private set; }
	public static bool BaoLaBanned { get; private set; }
	public static bool MomGone { get; private set; }
	public static bool MooedCart { get; private set; }
	public static bool FoundDad { get; private set; }
	public static bool HeardLikeHere { get; private set; }
	public static bool HasMilk { get; private set; }
	public static bool GaveMilk { get; private set; }
	public static bool SawLeopardFace { get; private set; }
	public static bool DidHeadbutt { get; private set; }
	public static int CartMoos { get; private set; }
	public static bool DidOpenCut { get; private set; }
	public static bool DidEatNag { get; private set; }
	public static int MooCount { get; private set; }
	public static int ExtraShares { get; private set; }
	public static bool Won { get; set; }
	public static bool SongWalk { get; set; }
	public static bool SawTail { get; private set; }
	public static bool WantHop { get; set; }
	public static bool InVoid => Beat is Beat.Dream or Beat.Farewell;
	public static int SceneScore
	{
		get
		{
			var n = 0;
			if ( DidOpenCut ) n++;
			if ( HeardLikeHere ) n++;
			if ( Bitten ) n++;
			if ( CreekJumped ) n++;
			if ( SawTail ) n++;
			if ( SawLeopardFace ) n++;
			if ( GaveMilk ) n++;
			if ( FedLeopard ) n++;
			if ( DidHeadbutt ) n++;
			if ( HerdSuspects ) n++;
			if ( BaoLaBanned ) n++;
			if ( MomGone ) n++;
			if ( MooedCart ) n++;
			if ( FoundDad ) n++;
			return n;
		}
	}
	public const int SceneTotal = 14;
	public static bool IsNight => Beat is Beat.Night or Beat.Suspect or Beat.Wolves or Beat.Ban;
	public static bool WolvesHunt => Beat is Beat.Wolves or Beat.Canyon;
	public static bool BaoLaFollows => FedLeopard && !BaoLaBanned && Beat < Beat.Farewell;
	public static float Growth => 0.62f + GrassEaten * 0.05f + Courage * 0.01f + ExtraShares * 0.03f + (FoundDad ? 0.2f : 0);
	public static float WalkMul => 0.80f + GrassEaten * 0.018f + Courage * 0.008f;
	public static float PlayTime { get; private set; }

	public static readonly Vector3 CampPos = new( 0, 0, 24 );
	public static readonly Vector3 DreamPos = new( 0, 2800, 16 );
	public static readonly Vector3 SnakePos = new( 280, 20, 24 );
	public static readonly Vector3 WakePos = new( 40, 40, 24 );
	public static readonly Vector3 DadPos = new( 160, 860, 24 );

	public static Line[] Dialogue { get; private set; }
	public static int DialogueIndex { get; private set; }
	public static string Speaker { get; private set; }
	public static bool Talking => Dialogue is { Length: > 0 };
	public static string PromptCn { get; private set; }
	public static string PromptEn { get; private set; }
	public static Action Teleport;
	public static Action MoodChanged;

	public static string ChapterCn => Beat switch
	{
		Beat.Migrate => "迁徙",
		Beat.Dream => "纯黑的梦",
		Beat.Snake => "小绳头",
		Beat.Creek => "小溪",
		Beat.Night => "豹拉",
		Beat.Suspect => "怀疑",
		Beat.Wolves => "狼来了",
		Beat.Ban => "不许跟着",
		Beat.Canyon => "峡谷",
		Beat.Cart => "台车",
		Beat.Farewell => "道别",
		Beat.Wake => "醒来",
		Beat.Reunion => "团聚",
		_ => "雨后轻风有香"
	};

	public static string ChapterEn => Beat switch
	{
		Beat.Migrate => "The walk",
		Beat.Dream => "Black dream",
		Beat.Snake => "Little Rope-Head",
		Beat.Creek => "The creek",
		Beat.Night => "Bao La",
		Beat.Suspect => "They doubt him",
		Beat.Wolves => "Wolves come",
		Beat.Ban => "Don't follow",
		Beat.Canyon => "Canyon",
		Beat.Cart => "The cart",
		Beat.Farewell => "Goodbye",
		Beat.Wake => "Wake",
		Beat.Reunion => "Together",
		_ => "After rain"
	};

	public static Line QuestLine => Lines.Quest( Beat );

	public static Line CurrentLine =>
		Talking && DialogueIndex >= 0 && DialogueIndex < Dialogue.Length
			? Dialogue[DialogueIndex]
			: default;

	public static void Reset()
	{
		Beat = Beat.Migrate;
		GrassEaten = 0;
		GrassBag = 0;
		Courage = 0;
		Bitten = false;
		CreekJumped = false;
		FedLeopard = false;
		HerdSuspects = false;
		BaoLaBanned = false;
		MomGone = false;
		MooedCart = false;
		FoundDad = false;
		HeardLikeHere = false;
		HasMilk = false;
		GaveMilk = false;
		SawLeopardFace = false;
		DidHeadbutt = false;
		CartMoos = 0;
		DidOpenCut = false;
		DidEatNag = false;
		SawTail = false;
		WantHop = false;
		MooCount = 0;
		ExtraShares = 0;
		Won = false;
		SongWalk = false;
		PlayTime = 0;
		ClearTalk();
		SetPrompt( "", "" );
	}

	public static void SetPrompt( string cn, string en )
	{
		cn ??= "";
		en ??= "";
		if ( PromptCn == cn && PromptEn == en )
			return;

		PromptCn = cn;
		PromptEn = en;
	}

	public static void Tick( float dt )
	{
		if ( GameFlow.Screen != GameScreen.Playing )
			return;

		PlayTime += dt;
		_prove += dt;
		if ( _prove > 44f && !Talking && !FilmCut.Playing )
		{
			_prove = 0;
			Chatter.Say( "旁白 / Narrator", Chatter.Pick( Lines.TimeProve ), 2.4f );
		}

		if ( Beat == Beat.Reunion && !Won && !Talking )
			GameFlow.Win();
	}

	static float _prove;

	public static void Talk( string speaker, Line[] lines )
	{
		if ( lines is not { Length: > 0 } )
			return;

		Speaker = speaker;
		Dialogue = lines;
		DialogueIndex = 0;
	}

	public static bool AdvanceTalk()
	{
		if ( !Talking )
			return false;

		DialogueIndex++;
		if ( DialogueIndex < Dialogue.Length )
			return false;

		ClearTalk();
		OnTalkEnd();
		return true;
	}

	public static void ClearTalk()
	{
		Dialogue = null;
		DialogueIndex = 0;
		Speaker = "";
	}

	static void OnTalkEnd()
	{
		if ( Beat == Beat.Migrate && !DidOpenCut )
		{
			DidOpenCut = true;
			FilmCut.Play( CutId.OpenEyes );
		}
		else if ( Beat == Beat.Dream )
			LeaveDream();
		else if ( Beat == Beat.Cart && MooedCart )
			EnterFarewell();
		else if ( Beat == Beat.Farewell )
			WakeUp();
		else if ( Beat == Beat.Reunion && !Won )
			GameFlow.Win();
	}

	public static void HearLikeHere()
	{
		if ( HeardLikeHere )
			return;
		HeardLikeHere = true;
		Talk( "云玎 / Yun Ding", Lines.LikeHere );
		GameAudio.PlayClip( GameAudio.LikeHere );
	}

	public static void EnterDreamFromCut()
	{
		if ( Beat != Beat.Migrate )
			return;
		Advance( Beat.Dream );
		Courage += 4;
		Trophies.Unlock( "yun_ding" );
		Teleport?.Invoke();
		MoodChanged?.Invoke();
	}

	public static void FinishFarewellCut()
	{
		if ( Beat == Beat.Farewell )
			WakeUp();
	}

	public static void GiveMilk()
	{
		if ( HasMilk )
		{
			Talk( "牛妈妈 / Mama", new[] { new Line( "杯子已经给你了。", "Cup already give you." ) } );
			return;
		}

		HasMilk = true;
		Talk( "牛妈妈 / Mama", Lines.MomMilk );
		GameAudio.PlayClip( GameAudio.MamaOk );
	}

	public static void PourMilk()
	{
		if ( GaveMilk )
			return;
		if ( !HasMilk )
		{
			Talk( "豹拉 / Bao La", new[] { new Line( "先去找妈妈要奶。", "First go find Mama ask milk." ) } );
			return;
		}

		GaveMilk = true;
		HasMilk = false;
		Courage += 6;
		FilmCut.Play( CutId.MilkDad );
	}

	public static bool TryLeopardMeet()
	{
		if ( !SawLeopardFace )
		{
			SawLeopardFace = true;
			FilmCut.Play( CutId.LeopardFace );
			return true;
		}

		if ( !GaveMilk )
		{
			PourMilk();
			return true;
		}

		if ( !FedLeopard )
			return FeedLeopard();

		return false;
	}

	static float _eatComboUntil;
	static int _eatCombo;

	public static void MarkEatNag() => DidEatNag = true;
	public static void MarkTail() => SawTail = true;

	public static void CourageBoost( int n )
	{
		Courage = Math.Min( 100, Courage + Math.Max( 0, n ) );
	}

	public static void EatGrass()
	{
		GrassEaten++;
		GrassBag++;
		Courage = Math.Min( 100, Courage + 2 );
		if ( PlayTime <= _eatComboUntil )
			_eatCombo++;
		else
			_eatCombo = 1;
		_eatComboUntil = PlayTime + 8f;

		if ( _eatCombo >= 3 )
		{
			_eatCombo = 0;
			Courage = Math.Min( 100, Courage + 3 );
			Chatter.Say( "牛来 / Niu Lai", new Line( "连吃三丛。我明显大了。", "Eat three tuft. I obvious bigger." ), 3.2f );
			GameAudio.PlayCrowd( GameAudio.CrowdMilk );
		}
		else
		{
			Chatter.Say( "草 / Grass", Chatter.Pick( Lines.Grass ) );
		}
	}

	public static bool ShareAgain()
	{
		if ( !FedLeopard || GrassBag <= 0 || BaoLaBanned )
			return false;

		GrassBag--;
		ExtraShares++;
		Courage = Math.Min( 100, Courage + 1 );
		Chatter.Say( "豹拉 / Bao La", Chatter.Pick( Lines.ExtraMilk ), 3.1f );
		GameAudio.PlayClip( GameAudio.Milk, 0.8f );
		return true;
	}

	public static void MooWorld( string nearId )
	{
		MooCount++;
		if ( Beat == Beat.Cart )
		{
			MooCart();
			return;
		}

		GameAudio.PlayClip( GameAudio.Moo );
		if ( MooCount == 8 || MooCount == 20 )
			GameAudio.PlayCrowd( GameAudio.CrowdCart );

		var line = nearId switch
		{
			"mom" => new Line( "你慢点。别对着妈妈哞。", "You slow a bit. Don't moo at Mama." ),
			"snake" => new Line( "小绳头抖了一下。还是绳子。", "Little Rope-Head shake once. Still rope." ),
			"leopard" => new Line( "急什么。我也想哞。", "Rush what. I also want moo." ),
			"rock" => new Line( "狼没理。石头也没理。", "Wolf not care. Rock also not care." ),
			"lark" => new Line( "嗯。", "Ng." ),
			"cart" => new Line( "它听见表扬。又大了一点。", "It hear praise. Bigger a little." ),
			"niuer" => new Line( "身份不对。哞也不对。", "Identity not right. Moo also not right." ),
			_ => Chatter.Pick( Lines.MooLines )
		};
		Chatter.Say( "哞 / Moo", line );
	}

	public static void MeetLark()
	{
		if ( Beat != Beat.Migrate )
			return;

		Advance( Beat.Dream );
		Courage += 4;
		Trophies.Unlock( "yun_ding" );
		Talk( "云玎 / Yun Ding", Lines.Dream );
		GameAudio.PlayClip( GameAudio.LarkNg );
		Teleport?.Invoke();
		MoodChanged?.Invoke();
	}

	public static void LeaveDream()
	{
		if ( Beat != Beat.Dream )
			return;

		Advance( Beat.Snake );
		Teleport?.Invoke();
		MoodChanged?.Invoke();
	}

	public static void GetBitten()
	{
		if ( Bitten )
			return;

		Bitten = true;
		Courage += 6;
		Talk( "小绳头 / Little Rope-Head", Lines.SnakeBite );
		if ( Beat == Beat.Snake )
			Advance( Beat.Creek );
	}

	public static void JumpCreek()
	{
		if ( CreekJumped )
			return;

		CreekJumped = true;
		Courage += 10;
		WantHop = true;
		Talk( "旁白 / Narrator", Lines.Creek );
		if ( Beat == Beat.Creek )
		{
			Advance( Beat.Night );
			MoodChanged?.Invoke();
		}
	}

	public static bool FeedLeopard()
	{
		if ( FedLeopard )
			return false;
		if ( GrassBag <= 0 )
		{
			Trophies.Unlock( "empty_bag" );
			return false;
		}

		GrassBag--;
		FedLeopard = true;
		Courage += 8;
		Talk( "豹拉 / Bao La", Lines.Leopard );
		if ( !DidHeadbutt )
		{
			DidHeadbutt = true;
			FilmCut.Play( CutId.Headbutt );
		}
		if ( Beat == Beat.Night && GaveMilk )
			Advance( Beat.Suspect );
		return true;
	}

	public static void HearSuspect()
	{
		if ( HerdSuspects )
			return;

		HerdSuspects = true;
		Courage += 3;
		Talk( "牛二 / Niu Er", Lines.Suspect );
		FilmCut.Play( CutId.TooClose );
		if ( Beat == Beat.Suspect )
			Advance( Beat.Wolves );
		MoodChanged?.Invoke();
	}

	public static void FirstWolfWave()
	{
		if ( Beat != Beat.Wolves )
			return;

		Talk( "旁白 / Narrator", Lines.WolvesSave );
		Advance( Beat.Ban );
	}

	public static void BanBaoLa()
	{
		if ( BaoLaBanned )
			return;

		BaoLaBanned = true;
		Talk( "牛群 / The herd", Lines.Ban );
		if ( Beat == Beat.Ban )
			Advance( Beat.Canyon );
	}

	public static void HearMom()
	{
		if ( MomGone )
			return;

		MomGone = true;
		Courage += 12;
		Talk( "牛妈妈 / Mama", Lines.MomFarewell );
		GameAudio.PlayClip( GameAudio.CanRest );
		if ( Beat == Beat.Canyon )
			Advance( Beat.Cart );
	}

	public static void MooCart()
	{
		CartMoos++;
		GameAudio.PlayClip( GameAudio.Moo );
		GameAudio.PlayCrowd( GameAudio.CrowdCart );
		if ( CartMoos == 1 )
			FilmCut.Play( CutId.NoRoad );

		if ( CartMoos < 3 )
		{
			Talk( "台车 / The Cart", new[]
			{
				new Line( $"哞 ×{CartMoos}。它更大了。", $"MOO ×{CartMoos}. It more big." )
			} );
			return;
		}

		if ( MooedCart )
			return;

		MooedCart = true;
		Courage += 14;
		Talk( "云玎 / Yun Ding", Lines.Cart );
	}

	static void EnterFarewell()
	{
		Advance( Beat.Farewell );
		Teleport?.Invoke();
		MoodChanged?.Invoke();
		FilmCut.Play( CutId.Ng );
	}

	public static void WakeUp()
	{
		if ( Beat != Beat.Farewell )
			return;

		Advance( Beat.Wake );
		Courage += 6;
		Talk( "旁白 / Narrator", Lines.Wake );
		Teleport?.Invoke();
		MoodChanged?.Invoke();
	}

	public static void MeetDad()
	{
		if ( FoundDad )
			return;

		FoundDad = true;
		Courage += 10;
		Talk( "牛爸爸 / Cow Father", Lines.Dad );
		Advance( Beat.Reunion );
		SongWalk = true;
		GameAudio.PlaySong();
	}

	public static void Advance( Beat next )
	{
		if ( next > Beat )
			Beat = next;
	}
}