MainHud/BlackjackEventRunner.cs

A static manager that runs a simple Blackjack mini-game between two Player objects. It deals random cards, presents a choice popup for Hit or Stand, resolves AI drawing rules, and applies scaled HP damage via CombatManager based on prestige from GameManager.

NetworkingFile Access
using Keyboard_Warriors_.Models;
using Keyboard_Warriors_.Systems;
using Sandbox;
using System;

namespace Keyboard_Warriors_.Managers;

public static class BlackjackEventRunner
{
	private static Player cachedPlayer;
	private static Player cachedAI;
	private static int secretAITotal;

	// In Blackjack, 11 represents an Ace. 
	// Random.Shared.Next(1, 12) generates numbers 1 through 11 inclusive.
	private static int DrawCard() => Random.Shared.Next( 1, 12 );

	public static void StartEvent( Player player, Player ai )
	{
		GameManager.AddLog( "** A game of BLACKJACK begins! **" );

		cachedPlayer = player;
		cachedAI = ai;

		// Deal starting hands securely
		int currentScalePlayerTotal = DrawCard() + DrawCard();
		secretAITotal = DrawCard() + DrawCard();

		GameManager.AddLog( $"You are dealt cards totaling: {currentScalePlayerTotal}" );
		GameManager.AddLog( $"AI is showing: {secretAITotal / 2} (one card hidden)" );
		GameManager.AddLog( "⚠️ AWAITING HIT/STAND PROTOCOL CHOICE..." );

		// Kick off the interactive turn loop
		RunBlackjackTurnLoop( currentScalePlayerTotal );
	}

	private static void RunBlackjackTurnLoop( int runningPlayerTotal )
	{
		// 1. Safely resolve your scene instance to read current progression statistics for the bust check
		var manager = Sandbox.Game.ActiveScene?.Get<GameManager>();
		int prestige = manager != null ? manager.CurrentPrestige : 0;
		int scaledWinDamage = 25 + (prestige * 3);

		// Immediate containment check if a fresh Hit pushes them over the limit
		if ( runningPlayerTotal > 21 )
		{
			GameManager.AddLog( $"** Total: {runningPlayerTotal} - You BUST! AI wins automatically. -{scaledWinDamage} HP. **" );
			CombatManager.applyDamage( ref cachedPlayer, scaledWinDamage );
			GameManager.AddLog( $"{cachedPlayer.name} | HP: {cachedPlayer.hp} | Armor: {cachedPlayer.defense}" );
			return;
		}

		var blackjackEvent = new ChoiceEvent
		{
			Title = "🃏 CYBER BLACKJACK EMULATION",
			PromptText = $"Your current hand value sits at: {runningPlayerTotal}. The AI is presenting a split risk window. Will you request an additional card packet or lock this calculation down?",
		};

		// --- OPTION 1: HIT ---
		blackjackEvent.Options.Add( new EventOption
		{
			ButtonText = "➕ REQUEST CARD (HIT)",
			ActionToExecute = async () =>
			{
				int freshCard = DrawCard();
				int updatedTotal = runningPlayerTotal + freshCard;
				GameManager.AddLog( $"You draw a {freshCard}. Total: {updatedTotal}" );

				// Force a tiny frame delay so s&box can safely process the UI transition
				await GameTask.Delay( 50 );

				// Clean state-safe recursion
				RunBlackjackTurnLoop( updatedTotal );
			}
		} );

		// --- OPTION 2: STAND ---
		blackjackEvent.Options.Add( new EventOption
		{
			ButtonText = "🔒 STAND WITH TOTAL",
			ActionToExecute = () =>
			{
				ResolveDealerAndMatch( runningPlayerTotal );
			}
		} );

		GameManager.Instance.TriggerChoicePopup( blackjackEvent );
	}

	private static void ResolveDealerAndMatch( int finalPlayerTotal )
	{
		// 2. Safely resolve your scene instance to read current progression statistics for match resolution
		var manager = Sandbox.Game.ActiveScene?.Get<GameManager>();
		int prestige = manager != null ? manager.CurrentPrestige : 0;

		// 3. Lineally scale your payout variables (+5 for natural Blackjack, +3 for standard win, +2 for ties per level)
		int scaledBlackjackDamage = 40 + (prestige * 5);
		int scaledWinDamage = 25 + (prestige * 3);
		int scaledTieDamage = 10 + (prestige * 2);

		// Direct immediate check for natural Blackjacks
		if ( finalPlayerTotal == 21 )
		{
			GameManager.AddLog( $"** BLACKJACK!! AI loses -{scaledBlackjackDamage} HP! **" );
			CombatManager.applyDamage( ref cachedAI, scaledBlackjackDamage );
			return;
		}

		GameManager.AddLog( $"You stand with {finalPlayerTotal}." );
		GameManager.AddLog( $"AI reveals hidden total: {secretAITotal}" );

		int aiRunningTotal = secretAITotal;
		while ( aiRunningTotal < 17 )
		{
			int card = DrawCard();
			aiRunningTotal += card;
			GameManager.AddLog( $"AI draws a {card}. Total: {aiRunningTotal}" );
		}

		// Core win/loss evaluation conditions using scaled values
		if ( aiRunningTotal == 21 )
		{
			GameManager.AddLog( $"** AI hits 21! You lose {scaledBlackjackDamage} HP! **" );
			CombatManager.applyDamage( ref cachedPlayer, scaledBlackjackDamage );
		}
		else if ( aiRunningTotal > 21 || finalPlayerTotal > aiRunningTotal )
		{
			GameManager.AddLog( $"** You win with {finalPlayerTotal} vs {aiRunningTotal}! AI loses {scaledWinDamage} HP! **" );
			CombatManager.applyDamage( ref cachedAI, scaledWinDamage );
		}
		else if ( aiRunningTotal > finalPlayerTotal )
		{
			GameManager.AddLog( $"** AI wins with {aiRunningTotal} vs {finalPlayerTotal}! You lose {scaledWinDamage} HP! **" );
			CombatManager.applyDamage( ref cachedPlayer, scaledWinDamage );
		}
		else
		{
			GameManager.AddLog( $"** Tie! Both lose {scaledTieDamage} HP. **" );
			CombatManager.applyDamage( ref cachedPlayer, scaledTieDamage );
			CombatManager.applyDamage( ref cachedAI, scaledTieDamage );
		}

		GameManager.AddLog( $"{cachedPlayer.name} | HP: {cachedPlayer.hp} | Armor: {cachedPlayer.defense}" );
		GameManager.AddLog( $"{cachedAI.name} | HP: {cachedAI.hp} | Armor: {cachedAI.defense}" );
	}
}