Add achievement system with unlock and UI popup functionality (#8)

This commit is contained in:
2025-08-31 00:50:53 +02:00
committed by GitHub
parent 88c7a0a055
commit 53b5f968fd
46 changed files with 464 additions and 243 deletions

View File

@@ -0,0 +1,105 @@
using Godot;
using Godot.Collections;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.Autoloads;
public partial class AchievementManager : Node
{
[Export] private string AchievementsFolderPath = "res://achievements/";
[Export] private PackedScene AchievementPopupScene { get; set; }
private System.Collections.Generic.Dictionary<string, AchievementResource> _achievements = new();
private Array<string> _unlockedAchievementIds = [];
private GameManager _gameManager;
public override void _Ready()
{
_gameManager = GetNode<GameManager>("/root/GameManager");
LoadAchievementsFromFolder();
LoadUnlockedAchievements();
}
private void LoadAchievementsFromFolder()
{
using var dir = DirAccess.Open(AchievementsFolderPath);
if (dir == null)
{
GD.PrintErr($"AchievementManager: Could not open achievements folder at '{AchievementsFolderPath}'");
return;
}
dir.ListDirBegin();
var fileName = dir.GetNext();
while (fileName != "")
{
if (!dir.CurrentIsDir() && fileName.EndsWith(".tres"))
{
var achievement = GD.Load<AchievementResource>(AchievementsFolderPath + fileName);
if (achievement != null)
{
_achievements.TryAdd(achievement.Id, achievement);
}
}
fileName = dir.GetNext();
}
}
public void UnlockAchievement(string achievementId)
{
if (!_achievements.TryGetValue(achievementId, out var achievement))
{
GD.PrintErr($"Attempted to unlock non-existent achievement: '{achievementId}'");
return;
}
if (_unlockedAchievementIds.Contains(achievementId))
{
return; // Already unlocked
}
// 1. Mark as unlocked internally
_unlockedAchievementIds.Add(achievementId);
GD.Print($"Achievement Unlocked: {achievement.DisplayName}");
// 2. Show the UI popup
if (AchievementPopupScene != null)
{
var popup = AchievementPopupScene.Instantiate<scripts.UI.AchievementPopup>();
GetTree().Root.AddChild(popup);
_ = popup.ShowAchievement(achievement);
}
// 3. Call SteamManager if it's available
if (SteamManager.IsSteamInitialized)
{
SteamManager.UnlockAchievement(achievement.Id);
}
// 4. Save progress
SaveUnlockedAchievements();
}
public void LockAchievement(string achievementId)
{
if (_unlockedAchievementIds.Contains(achievementId))
{
_unlockedAchievementIds.Remove(achievementId);
SaveUnlockedAchievements();
}
}
private void SaveUnlockedAchievements()
{
_gameManager.PlayerState["unlocked_achievements"] = _unlockedAchievementIds;
// You might want to trigger a save game here, depending on your SaveSystem
}
private void LoadUnlockedAchievements()
{
if (_gameManager.PlayerState.TryGetValue("unlocked_achievements", out var unlocked))
{
_unlockedAchievementIds = (Array<string>)unlocked;
}
}
}

View File

@@ -0,0 +1 @@
uid://c4vvuqnx5y33u

View File

@@ -10,10 +10,12 @@ public partial class ConsoleManager : Node
private GameManager _gameManager;
private SkillManager _skillManager;
private SkillUnlockerComponent _skillUnlockerComponent;
private AchievementManager _achievementManager;
public override void _Ready()
{
_gameManager = GetNode<GameManager>("/root/GameManager");
_achievementManager = GetNode<AchievementManager>("/root/AchievementManager");
RegisterConsoleCommands();
}
@@ -154,5 +156,18 @@ public partial class ConsoleManager : Node
{
_gameManager.OnLevelComplete();
}
[ConsoleCommand("unlock_achievement", "Unlocks an achievement by its ID.")]
private void UnlockAchievementCommand(string achievementId)
{
_achievementManager.UnlockAchievement(achievementId);
LimboConsole.Info($"Attempted to unlock achievement '{achievementId}'.");
}
[ConsoleCommand("reset_achievement", "Resets (locks) an achievement by its ID.")]
private void ResetAchievementCommand(string achievementId)
{
_achievementManager.LockAchievement(achievementId);
}
}

77
Autoloads/SteamManager.cs Normal file
View File

@@ -0,0 +1,77 @@
using System;
using Godot;
using Steamworks;
using Steamworks.Data;
namespace Mr.BrickAdventures.Autoloads;
public partial class SteamManager : Node
{
private const uint AppId = 3575090;
public static string PlayerName { get; private set; } = "Player";
public static bool IsSteamInitialized { get; private set; } = false;
public override void _Ready()
{
try
{
SteamClient.Init(AppId);
IsSteamInitialized = true;
PlayerName = SteamClient.Name;
GD.Print($"Steam initialized successfully for user: {PlayerName}");
}
catch (Exception e)
{
GD.PushError("Failed to initialize Steamworks. Is Steam running?");
GD.PushError(e.Message);
IsSteamInitialized = false;
}
}
public override void _Process(double delta)
{
if (IsSteamInitialized) SteamClient.RunCallbacks();
}
public override void _Notification(int what)
{
if (what == NotificationWMCloseRequest)
{
if (IsSteamInitialized)
{
SteamClient.Shutdown();
}
GetTree().Quit();
}
}
public static void UnlockAchievement(string achievementId)
{
if (!IsSteamInitialized)
{
GD.Print($"Steam not initialized. Cannot unlock achievement '{achievementId}'.");
return;
}
var ach = new Achievement(achievementId);
if (ach.State)
{
GD.Print($"Achievement '{achievementId}' is already unlocked.");
return;
}
if (ach.Trigger())
{
SteamUserStats.StoreStats();
GD.Print($"Successfully triggered achievement: {ach.Name} ({ach.Identifier})");
}
else
{
GD.PrintErr($"Failed to trigger achievement: {achievementId}");
}
}
}

View File

@@ -0,0 +1 @@
uid://b5abjlnbia63q