MainHud/AchievementManager.cs

Static AchievementManager for the HUD. Checks various conditions (damage taken, win/loss streaks, chosen weapon, jackpot, player/AI HP) and unlocks corresponding s&box achievements via Sandbox.Services.Achievements, returning a list of text lines for display including an ASCII banner when any achievements unlocked.

NetworkingFile Access
using Keyboard_Warriors_.Models;
using Keyboard_Warriors_.Systems;
using System;
using System.Collections.Generic;
using System.Linq; // 🚀 Required to use .Any() to check the list!

namespace Keyboard_Warriors_.Managers
{
	public static class AchievementManager
	{
		public static List<string> CheckAchievements( bool tookDamage, int winStreak, int lossStreak, Weapon chosenWeapon, Player player, Player ai, ref bool hitJackpot )
		{
			List<string> unlocked = new List<string>();

			// Grab the active GameManager instance out of the scene
			var manager = Sandbox.Game.ActiveScene.Get<GameManager>();

			// 1. Steel Defense
			if ( manager != null && !manager.HasPlayerTakenAnyDamage )
			{
				// Check if the achievement is NOT already unlocked in s&box's system
				if ( !Sandbox.Services.Achievements.All.Any( a => a.Name == "steel_defense" && a.IsUnlocked ) )
				{
					Sandbox.Services.Achievements.Unlock( "steel_defense" );
					unlocked.Add( "\n🏆 ACHIEVEMENT UNLOCKED: 'Steel Defense'" );
					unlocked.Add( "\"This must be the Battle of Trenton. Zero damage taken!\"" );
				}
			}

			// 2. Prisoner Streak
			if ( winStreak == 5 )
			{
				if ( !Sandbox.Services.Achievements.All.Any( a => a.Name == "prisoner_streak" && a.IsUnlocked ) )
				{
					Sandbox.Services.Achievements.Unlock( "prisoner_streak" );
					unlocked.Add( "\n🏆 ACHIEVEMENT UNLOCKED: 'Prisoner Streak'" );
					unlocked.Add( "\"5 wins in a row? Prisoners could do better!\"" );
				}
			}

			// 3. HAL's Worst Nightmare
			if ( winStreak == 10 )
			{
				if ( !Sandbox.Services.Achievements.All.Any( a => a.Name == "hals_nightmare" && a.IsUnlocked ) )
				{
					Sandbox.Services.Achievements.Unlock( "hals_nightmare" );
					unlocked.Add( "\n🏆 ACHIEVEMENT UNLOCKED: 'HAL's Worst Nightmare'" );
					unlocked.Add( "\"10 straight wins? AI's are starting to recognize fear!\"" );
				}
			}

			// 4. New Assassin
			if ( chosenWeapon?.name == "Dagger" && winStreak == 5 )
			{
				if ( !Sandbox.Services.Achievements.All.Any( a => a.Name == "new_assassin" && a.IsUnlocked ) )
				{
					Sandbox.Services.Achievements.Unlock( "new_assassin" );
					unlocked.Add( "\n🏆 ACHIEVEMENT UNLOCKED: 'New Assassin'" );
					unlocked.Add( "\"5 wins with a dagger.. Okay Altair!\"" );
				}
			}

			// 5. Final Breath
			if ( player.hp <= 0 && ai.hp <= 0 )
			{
				if ( !Sandbox.Services.Achievements.All.Any( a => a.Name == "final_breath" && a.IsUnlocked ) )
				{
					Sandbox.Services.Achievements.Unlock( "final_breath" );
					unlocked.Add( "\n🏆 ACHIEVEMENT UNLOCKED: 'Final Breath'" );
					unlocked.Add( "\"At 0 hp, you let one last strike or events caused the AI down with you. A tie in glory!\"" );
				}
			}

			// 6. Scratch of the Gods
			if ( hitJackpot )
			{
				if ( !Sandbox.Services.Achievements.All.Any( a => a.Name == "scratch_of_gods" && a.IsUnlocked ) )
				{
					Sandbox.Services.Achievements.Unlock( "scratch_of_gods" );
					unlocked.Add( "\n🏆 ACHIEVEMENT UNLOCKED: 'Scratch of the Gods'" );
					unlocked.Add( "\"You hit the jackpot during battle, the points are yours!\"" );
				}
				hitJackpot = false;
			}

			if ( lossStreak == 5 ) // Ensure your method signature now includes 'int lossStreak'
			{
				if ( !Sandbox.Services.Achievements.All.Any( a => a.Name == "turn_off_on" && a.IsUnlocked ) )
				{
					Sandbox.Services.Achievements.Unlock( "turn_off_on" );
					unlocked.Add( "\n🏆 ACHIEVEMENT UNLOCKED: 'Try Turning It Off and On Again'" );
					unlocked.Add( "\"AI: 5 losses in a row... Is all well up there?\"" );
				}
			}

			if ( unlocked.Count > 0 )
			{
				string bannerLines = @"
   _   _____  _   _ _____ _____ _   _ _____ ______  _____ _   _ _____   _   _ _   _  _ ___ _____ _____  _   __ _____ _____ _
 / _ \/  __ \| | | |_   _|  ___| | | |  ___|  \/  ||  ___| \ | |_   _| | | | | \ | || |   |  _  /  __ \| | / /|  ___|  _  \ |
/ /_\ \ /  \/| |_| | | | | |__ | | | | |__ | .  . || |__ |  \| | | |   | | | |  \| || |   | | | | /  \/| |/ / | |__ | | | | |
|  _  | |    |  _  | | | |  __|| | | |  __|| |\/| ||  __|| . ` | | |   | | | | . ` || |   | | | | |    |    \ |  __|| | | | |
| | | | \__/\| | | |_| |_| |___\ \_/ / |___| |  | || |___| |\  | | |   | |_| | |\  || |___\ \_/ / \__/\| |\  \| |___| |/ /|_|
\_| |_/\____/\_| |_/\___/\____/ \___/\____/\_|  |_/\____/\_| \_/ \_/    \___/\_| \_/\_____/\___/ \____/\_| \_/\____/|___/ (_)
";

				// Add each line of the banner to the beginning or end of the return list
				foreach ( var line in bannerLines.Split( '\n' ) )
				{
					unlocked.Add( line );
				}
			}

			return unlocked;
		}
	}
}