Compare commits

...

5 Commits

129 changed files with 8085 additions and 1136 deletions

View File

@@ -1,23 +1,22 @@
using Godot;
using Godot.Collections;
using Mr.BrickAdventures.scripts.Resources;
using Mr.BrickAdventures.scripts.State;
namespace Mr.BrickAdventures.Autoloads;
/// <summary>
/// Manages achievements using GameStateStore.
/// </summary>
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()
@@ -45,6 +44,14 @@ public partial class AchievementManager : Node
}
}
/// <summary>
/// Gets the list of unlocked achievement IDs from the store.
/// </summary>
private System.Collections.Generic.List<string> GetUnlockedIds()
{
return GameStateStore.Instance?.Player.UnlockedAchievements ?? new System.Collections.Generic.List<string>();
}
public void UnlockAchievement(string achievementId)
{
if (!_achievements.TryGetValue(achievementId, out var achievement))
@@ -53,13 +60,14 @@ public partial class AchievementManager : Node
return;
}
if (_unlockedAchievementIds.Contains(achievementId))
var unlockedIds = GetUnlockedIds();
if (unlockedIds.Contains(achievementId))
{
return; // Already unlocked
}
// 1. Mark as unlocked internally
_unlockedAchievementIds.Add(achievementId);
// 1. Mark as unlocked
unlockedIds.Add(achievementId);
GD.Print($"Achievement Unlocked: {achievement.DisplayName}");
// 2. Show the UI popup
@@ -75,31 +83,19 @@ public partial class AchievementManager : Node
{
SteamManager.UnlockAchievement(achievement.Id);
}
// 4. Save progress
SaveUnlockedAchievements();
}
public void LockAchievement(string achievementId)
{
if (_unlockedAchievementIds.Contains(achievementId))
var unlockedIds = GetUnlockedIds();
if (unlockedIds.Contains(achievementId))
{
_unlockedAchievementIds.Remove(achievementId);
SaveUnlockedAchievements();
unlockedIds.Remove(achievementId);
}
}
private void SaveUnlockedAchievements()
public bool IsAchievementUnlocked(string achievementId)
{
_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;
}
return GetUnlockedIds().Contains(achievementId);
}
}

View File

@@ -1,6 +1,5 @@
using Godot;
using Limbo.Console.Sharp;
using Mr.BrickAdventures.scripts;
using Mr.BrickAdventures;
using Mr.BrickAdventures.scripts.components;
namespace Mr.BrickAdventures.Autoloads;
@@ -14,69 +13,45 @@ public partial class ConsoleManager : Node
public override void _Ready()
{
_gameManager = GetNode<GameManager>("/root/GameManager");
_achievementManager = GetNode<AchievementManager>("/root/AchievementManager");
_skillManager = GetNode<SkillManager>("/root/SkillManager");
RegisterConsoleCommands();
_gameManager = GameManager.Instance;
_achievementManager = GetNode<AchievementManager>(Constants.AchievementManagerPath);
_skillManager = SkillManager.Instance;
}
public override void _ExitTree()
{
UnregisterConsoleCommands();
}
[ConsoleCommand("add_coins", "Adds a specified amount of coins to the player's total.")]
private void AddCoinsCommand(int amount)
{
_gameManager.AddCoins(amount);
LimboConsole.Info($"Increased coins by {amount}. Total coins: {_gameManager.GetCoins()}");
}
[ConsoleCommand("set_coins", "Sets the player's total coins to a specified amount.")]
private void SetCoinsCommand(int amount)
{
_gameManager.SetCoins(amount);
LimboConsole.Info($"Set coins to {amount}. Total coins: {_gameManager.GetCoins()}");
}
[ConsoleCommand("set_lives", "Sets the player's total lives to a specified amount.")]
private void SetLivesCommand(int amount)
{
_gameManager.SetLives(amount);
LimboConsole.Info($"Set lives to {amount}.");
}
[ConsoleCommand("add_lives", "Adds a specified amount of lives to the player's total.")]
private void AddLivesCommand(int amount)
{
_gameManager.AddLives(amount);
LimboConsole.Info($"Increased lives by {amount}. Total lives: {_gameManager.GetLives()}");
}
[ConsoleCommand("set_health", "Sets the player's health to a specified amount.")]
private void SetHealthCommand(float amount)
{
var playerHealthComponent = _gameManager.Player.GetNode<HealthComponent>("HealthComponent");
if (playerHealthComponent != null)
{
playerHealthComponent.Health = amount;
LimboConsole.Info($"Set player health to {amount}.");
}
else
{
LimboConsole.Warn("Player HealthComponent not found.");
}
}
[ConsoleCommand("reset_session", "Resets the current session state.")]
private void ResetSessionCommand()
{
_gameManager.ResetCurrentSessionState();
LimboConsole.Info("Current session state has been reset.");
}
[ConsoleCommand("unlock_skill", "Unlocks and activates a skill by its name.")]
private void UnlockSkillCommand(string skillName)
{
if (!GetSkillManagement()) return;
@@ -84,14 +59,12 @@ public partial class ConsoleManager : Node
var skill = _skillManager.GetSkillByName(skillName);
if (skill == null)
{
LimboConsole.Warn($"Skill '{skillName}' not found.");
return;
}
_gameManager.UnlockSkill(skill);
_skillManager.ActivateSkill(skill);
_skillUnlockerComponent.EmitSignal(SkillUnlockerComponent.SignalName.SkillUnlocked, skill);
LimboConsole.Info($"Skill '{skillName}' has been unlocked and activated.");
}
private bool GetSkillManagement()
@@ -99,7 +72,6 @@ public partial class ConsoleManager : Node
var player = _gameManager.Player;
if (player == null || !IsInstanceValid(player))
{
LimboConsole.Warn("Player node not found or is invalid.");
return false;
}
@@ -107,21 +79,17 @@ public partial class ConsoleManager : Node
if (_skillManager != null && _skillUnlockerComponent != null) return true;
LimboConsole.Warn("SkillManager or SkillUnlockerComponent not found on the player.");
return false;
}
[ConsoleCommand("unlock_all_skills", "Unlocks and activates all available skills.")]
private void UnlockAllSkillsCommand()
{
if (!GetSkillManagement()) return;
_skillUnlockerComponent.UnlockAllSkills();
LimboConsole.Info("All skills have been unlocked and activated.");
}
[ConsoleCommand("remove_skill", "Deactivates and removes a skill by its name.")]
private void RemoveSkillCommand(string skillName)
{
if (!GetSkillManagement()) return;
@@ -129,16 +97,13 @@ public partial class ConsoleManager : Node
var skill = _skillManager.GetSkillByName(skillName);
if (skill == null)
{
LimboConsole.Warn($"Skill '{skillName}' not found.");
return;
}
_gameManager.RemoveSkill(skill.Name);
_skillManager.DeactivateSkill(skill);
LimboConsole.Info($"Skill '{skillName}' has been deactivated.");
}
[ConsoleCommand("remove_all_skills", "Deactivates and removes all skills.")]
private void RemoveAllSkillsCommand()
{
if (!GetSkillManagement()) return;
@@ -148,23 +113,18 @@ public partial class ConsoleManager : Node
_gameManager.RemoveSkill(skill.Name);
_skillManager.DeactivateSkill(skill);
}
LimboConsole.Info("All skills have been deactivated.");
}
[ConsoleCommand("next_level", "Advances the game to the next level.")]
private void GoToNextLevelCommand()
{
_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);

View File

@@ -1,9 +1,155 @@
using Godot;
using Mr.BrickAdventures.scripts.components;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.Autoloads;
/// <summary>
/// Global event bus for decoupled communication between game systems.
/// Use the static Instance property for easy access from anywhere.
/// </summary>
public partial class EventBus : Node
{
/// <summary>
/// Singleton instance. Available after the autoload is initialized.
/// </summary>
public static EventBus Instance { get; private set; }
public override void _Ready()
{
Instance = this;
}
public override void _ExitTree()
{
if (Instance == this)
Instance = null;
}
#region Level Events
[Signal] public delegate void LevelStartedEventHandler(int levelIndex, Node currentScene);
[Signal] public delegate void LevelCompletedEventHandler(int levelIndex, Node currentScene, double completionTime);
[Signal] public delegate void LevelRestartedEventHandler(int levelIndex);
public static void EmitLevelStarted(int levelIndex, Node currentScene)
=> Instance?.EmitSignal(SignalName.LevelStarted, levelIndex, currentScene);
public static void EmitLevelCompleted(int levelIndex, Node currentScene, double completionTime)
=> Instance?.EmitSignal(SignalName.LevelCompleted, levelIndex, currentScene, completionTime);
public static void EmitLevelRestarted(int levelIndex)
=> Instance?.EmitSignal(SignalName.LevelRestarted, levelIndex);
#endregion
#region Player Events
[Signal] public delegate void PlayerSpawnedEventHandler(PlayerController player);
[Signal] public delegate void PlayerDiedEventHandler(Vector2 position);
[Signal] public delegate void PlayerDamagedEventHandler(float damage, float remainingHealth, Vector2 position);
[Signal] public delegate void PlayerHealedEventHandler(float amount, float newHealth, Vector2 position);
public static void EmitPlayerSpawned(PlayerController player)
=> Instance?.EmitSignal(SignalName.PlayerSpawned, player);
public static void EmitPlayerDied(Vector2 position)
=> Instance?.EmitSignal(SignalName.PlayerDied, position);
public static void EmitPlayerDamaged(float damage, float remainingHealth, Vector2 position)
=> Instance?.EmitSignal(SignalName.PlayerDamaged, damage, remainingHealth, position);
public static void EmitPlayerHealed(float amount, float newHealth, Vector2 position)
=> Instance?.EmitSignal(SignalName.PlayerHealed, amount, newHealth, position);
#endregion
#region Combat Events
[Signal] public delegate void EnemyDefeatedEventHandler(Node enemy, Vector2 position);
[Signal] public delegate void EnemyDamagedEventHandler(Node enemy, float damage, Vector2 position);
public static void EmitEnemyDefeated(Node enemy, Vector2 position)
=> Instance?.EmitSignal(SignalName.EnemyDefeated, enemy, position);
public static void EmitEnemyDamaged(Node enemy, float damage, Vector2 position)
=> Instance?.EmitSignal(SignalName.EnemyDamaged, enemy, damage, position);
#endregion
#region Collection Events
[Signal] public delegate void CoinCollectedEventHandler(int amount, Vector2 position);
[Signal] public delegate void ItemCollectedEventHandler(CollectableType itemType, float amount, Vector2 position);
[Signal] public delegate void ChildRescuedEventHandler(Vector2 position);
[Signal] public delegate void SkillCollectedEventHandler(SkillData skill, Vector2 position);
public static void EmitCoinCollected(int amount, Vector2 position)
=> Instance?.EmitSignal(SignalName.CoinCollected, amount, position);
public static void EmitItemCollected(CollectableType itemType, float amount, Vector2 position)
=> Instance?.EmitSignal(SignalName.ItemCollected, (int)itemType, amount, position);
public static void EmitChildRescued(Vector2 position)
=> Instance?.EmitSignal(SignalName.ChildRescued, position);
public static void EmitSkillCollected(SkillData skill, Vector2 position)
=> Instance?.EmitSignal(SignalName.SkillCollected, skill, position);
#endregion
#region Skill Events
[Signal] public delegate void SkillUnlockedEventHandler(string skillName, int level);
[Signal] public delegate void SkillActivatedEventHandler(string skillName);
[Signal] public delegate void SkillDeactivatedEventHandler(string skillName);
public static void EmitSkillUnlocked(string skillName, int level = 1)
=> Instance?.EmitSignal(SignalName.SkillUnlocked, skillName, level);
public static void EmitSkillActivated(string skillName)
=> Instance?.EmitSignal(SignalName.SkillActivated, skillName);
public static void EmitSkillDeactivated(string skillName)
=> Instance?.EmitSignal(SignalName.SkillDeactivated, skillName);
#endregion
#region Game State Events
[Signal] public delegate void GamePausedEventHandler();
[Signal] public delegate void GameResumedEventHandler();
[Signal] public delegate void GameSavedEventHandler();
[Signal] public delegate void GameStartedEventHandler();
[Signal] public delegate void GameContinuedEventHandler();
public static void EmitGamePaused()
=> Instance?.EmitSignal(SignalName.GamePaused);
public static void EmitGameResumed()
=> Instance?.EmitSignal(SignalName.GameResumed);
public static void EmitGameSaved()
=> Instance?.EmitSignal(SignalName.GameSaved);
public static void EmitGameStarted()
=> Instance?.EmitSignal(SignalName.GameStarted);
public static void EmitGameContinued()
=> Instance?.EmitSignal(SignalName.GameContinued);
#endregion
#region State Change Events
[Signal] public delegate void CoinsChangedEventHandler(int totalCoins);
[Signal] public delegate void LivesChangedEventHandler(int lives);
public static void EmitCoinsChanged(int totalCoins)
=> Instance?.EmitSignal(SignalName.CoinsChanged, totalCoins);
public static void EmitLivesChanged(int lives)
=> Instance?.EmitSignal(SignalName.LivesChanged, lives);
#endregion
}

View File

@@ -1,5 +1,7 @@
using System;
using System.Globalization;
using Godot;
using Mr.BrickAdventures.scripts.Resources;
using Mr.BrickAdventures.scripts.UI;
namespace Mr.BrickAdventures.Autoloads;
@@ -14,6 +16,18 @@ public partial class FloatingTextManager : Node
[Export] public Color CoinColor { get; set; } = new Color("#ebd320"); // Gold
[Export] public Color MessageColor { get; set; } = new Color("#ffffff"); // White
public static FloatingTextManager Instance { get; private set; }
public override void _Ready()
{
Instance = this;
}
public override void _ExitTree()
{
if (Instance == this) Instance = null;
}
public void ShowDamage(float amount, Vector2 position)
{
var text = Mathf.Round(amount * 100f).ToString(CultureInfo.InvariantCulture);
@@ -22,7 +36,7 @@ public partial class FloatingTextManager : Node
public void ShowHeal(float amount, Vector2 position)
{
var text = $"+{Mathf.Round(amount)}";
var text = $"+{Mathf.Round(amount * 100f).ToString(CultureInfo.InvariantCulture)}";
CreateFloatingText(text, position, HealColor);
}

View File

@@ -3,15 +3,20 @@ using Godot;
using Godot.Collections;
using Mr.BrickAdventures.scripts.components;
using Mr.BrickAdventures.scripts.Resources;
using Double = System.Double;
using Mr.BrickAdventures.scripts.State;
namespace Mr.BrickAdventures.Autoloads;
/// <summary>
/// Game orchestrator - handles scene management and game flow.
/// State is delegated to GameStateStore for better separation of concerns.
/// </summary>
public partial class GameManager : Node
{
[Export] public Array<PackedScene> LevelScenes { get; set; } = [];
public PlayerController Player {
public PlayerController Player
{
get => GetPlayer();
private set => _player = value;
}
@@ -19,25 +24,13 @@ public partial class GameManager : Node
private List<Node> _sceneNodes = [];
private PlayerController _player;
private SpeedRunManager _speedRunManager;
private EventBus _eventBus;
[Export]
public Dictionary PlayerState { get; set; } = new()
{
{ "coins", 0 },
{ "lives", 3 },
{ "current_level", 0 },
{ "completed_levels", new Array<int>() },
{ "unlocked_levels", new Array<int>() {0}},
{ "unlocked_skills", new Array<SkillData>() }
};
/// <summary>
/// Lazy accessor for GameStateStore - avoids initialization order issues.
/// </summary>
private GameStateStore Store => GameStateStore.Instance;
[Export]
public Dictionary CurrentSessionState { get; private set; } = new()
{
{ "coins_collected", 0 },
{ "skills_unlocked", new Array<SkillData>() }
};
public static GameManager Instance { get; private set; }
public override void _EnterTree()
{
@@ -47,6 +40,7 @@ public partial class GameManager : Node
public override void _ExitTree()
{
if (Instance == this) Instance = null;
GetTree().NodeAdded -= OnNodeAdded;
GetTree().NodeRemoved -= OnNodeRemoved;
_sceneNodes.Clear();
@@ -54,8 +48,8 @@ public partial class GameManager : Node
public override void _Ready()
{
_speedRunManager = GetNode<SpeedRunManager>("/root/SpeedRunManager");
_eventBus = GetNode<EventBus>("/root/EventBus");
Instance = this;
_speedRunManager = GetNode<SpeedRunManager>(Constants.SpeedRunManagerPath);
}
private void OnNodeAdded(Node node)
@@ -72,59 +66,73 @@ public partial class GameManager : Node
}
}
#region Coin Operations
public void AddCoins(int amount)
{
PlayerState["coins"] = Mathf.Max(0, (int)PlayerState["coins"] + amount);
}
public void SetCoins(int amount) => PlayerState["coins"] = Mathf.Max(0, amount);
public int GetCoins() => (int)PlayerState["coins"] + (int)CurrentSessionState["coins_collected"];
public void RemoveCoins(int amount)
if (Store != null)
{
var sessionCoins = (int)CurrentSessionState["coins_collected"];
if (amount <= sessionCoins)
{
CurrentSessionState["coins_collected"] = sessionCoins - amount;
Store.Player.Coins += amount;
EventBus.EmitCoinsChanged(Store.GetTotalCoins());
}
else
{
var remaining = amount - sessionCoins;
CurrentSessionState["coins_collected"] = 0;
PlayerState["coins"] = Mathf.Max(0, (int)PlayerState["coins"] - remaining);
}
PlayerState["coins"] = Mathf.Max(0, (int)PlayerState["coins"]);
}
public void AddLives(int amount) => PlayerState["lives"] = (int)PlayerState["lives"] + amount;
public void RemoveLives(int amount) => PlayerState["lives"] = (int)PlayerState["lives"] - amount;
public void SetLives(int amount) => PlayerState["lives"] = amount;
public int GetLives() => (int)PlayerState["lives"];
public bool IsSkillUnlocked(SkillData skill)
public void SetCoins(int amount)
{
return ((Array)PlayerState["unlocked_skills"]).Contains(skill)
|| ((Array)CurrentSessionState["skills_unlocked"]).Contains(skill);
if (Store != null)
{
Store.Player.Coins = Mathf.Max(0, amount);
EventBus.EmitCoinsChanged(Store.GetTotalCoins());
}
}
public int GetCoins() => Store?.GetTotalCoins() ?? 0;
public void RemoveCoins(int amount) => Store?.RemoveCoins(amount);
#endregion
#region Lives Operations
public void AddLives(int amount) => Store?.AddLives(amount);
public void RemoveLives(int amount) => Store?.RemoveLife();
public void SetLives(int amount)
{
if (Store != null)
{
Store.Player.Lives = amount;
EventBus.EmitLivesChanged(amount);
}
}
public int GetLives() => Store?.Player.Lives ?? 0;
#endregion
#region Skill Operations
public bool IsSkillUnlocked(SkillData skill) => Store?.IsSkillUnlocked(skill) ?? false;
public void UnlockSkill(SkillData skill)
{
if (!IsSkillUnlocked(skill))
((Array)PlayerState["unlocked_skills"]).Add(skill);
if (Store != null && !Store.IsSkillUnlocked(skill))
{
Store.Player.UnlockedSkills.Add(skill);
}
}
public void RemoveSkill(string skillName)
{
var arr = (Array)PlayerState["unlocked_skills"];
foreach (SkillData s in arr)
if (Store == null) return;
var skills = Store.Player.UnlockedSkills;
for (int i = 0; i < skills.Count; i++)
{
if (s.Name != skillName) continue;
arr.Remove(s);
if (skills[i].Name == skillName)
{
skills.RemoveAt(i);
break;
}
}
}
public void UnlockSkills(Array<SkillData> skills)
{
@@ -132,81 +140,82 @@ public partial class GameManager : Node
UnlockSkill(s);
}
public void ResetPlayerState()
public Array<SkillData> GetUnlockedSkills()
{
PlayerState = new Dictionary
{
{ "coins", 0 },
{ "lives", 3 },
{ "current_level", 0 },
{ "completed_levels", new Array<int>() },
{ "unlocked_levels", new Array<int>() {0}},
{ "unlocked_skills", new Array<SkillData>() },
{ "statistics", new Godot.Collections.Dictionary<string, Variant>()}
};
if (Store == null) return new Array<SkillData>();
var skills = Store.GetAllUnlockedSkills();
var result = new Array<SkillData>();
foreach (var s in skills) result.Add(s);
return result;
}
public void UnlockLevel(int levelIndex)
{
var unlocked = (Array)PlayerState["unlocked_levels"];
if (!unlocked.Contains(levelIndex)) unlocked.Add(levelIndex);
}
#endregion
#region Level Operations
public void UnlockLevel(int levelIndex) => Store?.UnlockLevel(levelIndex);
public void MarkLevelComplete(int levelIndex) => Store?.MarkLevelComplete(levelIndex);
public void TryToGoToNextLevel()
{
var next = (int)PlayerState["current_level"] + 1;
var unlocked = (Array)PlayerState["unlocked_levels"];
if (next < LevelScenes.Count && unlocked.Contains(next))
if (Store == null) return;
var next = Store.Session.CurrentLevel + 1;
if (next < LevelScenes.Count && Store.IsLevelUnlocked(next))
{
PlayerState["current_level"] = next;
Store.Session.CurrentLevel = next;
GetTree().ChangeSceneToPacked(LevelScenes[next]);
_eventBus.EmitSignal(EventBus.SignalName.LevelStarted, next, GetTree().CurrentScene);
EventBus.EmitLevelStarted(next, GetTree().CurrentScene);
}
}
public void MarkLevelComplete(int levelIndex)
{
UnlockLevel(levelIndex + 1);
var completed = (Array)PlayerState["completed_levels"];
if (!completed.Contains(levelIndex)) completed.Add(levelIndex);
}
#endregion
public void ResetCurrentSessionState()
{
CurrentSessionState = new Dictionary
{
{ "coins_collected", 0 },
{ "skills_unlocked", new Array<SkillData>() }
};
}
#region State Reset
public void ResetPlayerState() => Store?.ResetAll();
public void ResetCurrentSessionState() => Store?.ResetSession();
#endregion
#region Game Flow
public void RestartGame()
{
ResetPlayerState();
ResetCurrentSessionState();
Store?.ResetAll();
GetTree().ChangeSceneToPacked(LevelScenes[0]);
GetNode<SaveSystem>("/root/SaveSystem").SaveGame();
GetNode<SaveSystem>(Constants.SaveSystemPath).SaveGame();
}
public void QuitGame() => GetTree().Quit();
public void PauseGame() => Engine.TimeScale = 0;
public void ResumeGame() => Engine.TimeScale = 1;
public void PauseGame()
{
Engine.TimeScale = 0;
EventBus.EmitGamePaused();
}
public void ResumeGame()
{
Engine.TimeScale = 1;
EventBus.EmitGameResumed();
}
public void StartNewGame()
{
ResetPlayerState();
ResetCurrentSessionState();
Store?.ResetAll();
_speedRunManager?.StartTimer();
GetTree().ChangeSceneToPacked(LevelScenes[0]);
GetNode<SaveSystem>("/root/SaveSystem").SaveGame();
GetNode<SaveSystem>(Constants.SaveSystemPath).SaveGame();
EventBus.EmitGameStarted();
}
public void ContinueGame()
{
var save = GetNode<SaveSystem>("/root/SaveSystem");
var save = GetNode<SaveSystem>(Constants.SaveSystemPath);
if (!save.LoadGame())
{
GD.PrintErr("Failed to load game. Starting a new game instead.");
@@ -214,41 +223,38 @@ public partial class GameManager : Node
return;
}
var idx = (int)PlayerState["current_level"];
var idx = Store?.Session.CurrentLevel ?? 0;
if (idx < LevelScenes.Count)
{
GetTree().ChangeSceneToPacked(LevelScenes[idx]);
EventBus.EmitGameContinued();
}
else
{
GD.PrintErr("No levels unlocked to continue.");
}
}
public void OnLevelComplete()
{
var levelIndex = (int)PlayerState["current_level"];
MarkLevelComplete(levelIndex);
if (Store == null) return;
AddCoins((int)CurrentSessionState["coins_collected"]);
foreach (var s in (Array)CurrentSessionState["skills_unlocked"])
UnlockSkill((SkillData)s);
var levelIndex = Store.Session.CurrentLevel;
Store.MarkLevelComplete(levelIndex);
Store.CommitSessionCoins();
Store.CommitSessionSkills();
var completionTime = _speedRunManager?.GetCurrentLevelTime() ?? 0.0;
_eventBus.EmitSignal(EventBus.SignalName.LevelCompleted, levelIndex, GetTree().CurrentScene, completionTime);
EventBus.EmitLevelCompleted(levelIndex, GetTree().CurrentScene, completionTime);
ResetCurrentSessionState();
Store.ResetSession();
TryToGoToNextLevel();
GetNode<SaveSystem>("/root/SaveSystem").SaveGame();
GetNode<SaveSystem>(Constants.SaveSystemPath).SaveGame();
}
public Array<SkillData> GetUnlockedSkills()
{
var unlocked = (Array<SkillData>)PlayerState["unlocked_skills"];
var session = (Array<SkillData>)CurrentSessionState["skills_unlocked"];
if (session!.Count == 0) return unlocked;
if (unlocked!.Count == 0) return session;
var joined = new Array<SkillData>();
joined.AddRange(unlocked);
joined.AddRange(session);
return joined;
}
#endregion
#region Player Lookup
public PlayerController GetPlayer()
{
@@ -267,4 +273,6 @@ public partial class GameManager : Node
GD.PrintErr("PlayerController not found in the scene tree.");
return null;
}
#endregion
}

199
Autoloads/GameStateStore.cs Normal file
View File

@@ -0,0 +1,199 @@
using Godot;
using System.Collections.Generic;
using Mr.BrickAdventures.scripts.Resources;
using Mr.BrickAdventures.scripts.State;
namespace Mr.BrickAdventures.Autoloads;
/// <summary>
/// Central store for game state - single source of truth.
/// Use the static Instance property for easy access.
/// </summary>
public partial class GameStateStore : Node
{
/// <summary>
/// Singleton instance.
/// </summary>
public static GameStateStore Instance { get; private set; }
/// <summary>
/// Persistent player state (saved to disk).
/// </summary>
public PlayerState Player { get; set; } = new();
/// <summary>
/// Current session state (transient, reset on death/level complete).
/// </summary>
public SessionState Session { get; set; } = new();
public override void _Ready()
{
Instance = this;
}
public override void _ExitTree()
{
if (Instance == this)
Instance = null;
}
#region Coin Operations
/// <summary>
/// Gets total coins (saved + session).
/// </summary>
public int GetTotalCoins() => Player.Coins + Session.CoinsCollected;
/// <summary>
/// Adds coins to the session (not saved until level complete).
/// </summary>
public void AddSessionCoins(int amount)
{
Session.CoinsCollected += amount;
EventBus.EmitCoinsChanged(GetTotalCoins());
}
/// <summary>
/// Commits session coins to player state.
/// </summary>
public void CommitSessionCoins()
{
Player.Coins += Session.CoinsCollected;
Session.CoinsCollected = 0;
}
/// <summary>
/// Removes coins, first from session then from saved.
/// </summary>
public void RemoveCoins(int amount)
{
if (amount <= Session.CoinsCollected)
{
Session.CoinsCollected -= amount;
}
else
{
var remaining = amount - Session.CoinsCollected;
Session.CoinsCollected = 0;
Player.Coins = Mathf.Max(0, Player.Coins - remaining);
}
EventBus.EmitCoinsChanged(GetTotalCoins());
}
#endregion
#region Lives Operations
/// <summary>
/// Decrements lives by 1.
/// </summary>
public void RemoveLife()
{
Player.Lives = Mathf.Max(0, Player.Lives - 1);
EventBus.EmitLivesChanged(Player.Lives);
}
/// <summary>
/// Adds lives.
/// </summary>
public void AddLives(int amount)
{
Player.Lives += amount;
EventBus.EmitLivesChanged(Player.Lives);
}
#endregion
#region Level Operations
/// <summary>
/// Unlocks a level for access.
/// </summary>
public void UnlockLevel(int levelIndex)
{
if (!Player.UnlockedLevels.Contains(levelIndex))
Player.UnlockedLevels.Add(levelIndex);
}
/// <summary>
/// Marks a level as completed and unlocks the next.
/// </summary>
public void MarkLevelComplete(int levelIndex)
{
if (!Player.CompletedLevels.Contains(levelIndex))
Player.CompletedLevels.Add(levelIndex);
UnlockLevel(levelIndex + 1);
}
/// <summary>
/// Checks if a level is unlocked.
/// </summary>
public bool IsLevelUnlocked(int levelIndex) => Player.UnlockedLevels.Contains(levelIndex);
#endregion
#region Skill Operations
/// <summary>
/// Checks if a skill is unlocked (saved or session).
/// </summary>
public bool IsSkillUnlocked(SkillData skill)
{
return Player.UnlockedSkills.Contains(skill) || Session.SkillsUnlocked.Contains(skill);
}
/// <summary>
/// Unlocks a skill in the session.
/// </summary>
public void UnlockSkillInSession(SkillData skill)
{
if (!IsSkillUnlocked(skill))
Session.SkillsUnlocked.Add(skill);
}
/// <summary>
/// Commits session skills to player state.
/// </summary>
public void CommitSessionSkills()
{
foreach (var skill in Session.SkillsUnlocked)
{
if (!Player.UnlockedSkills.Contains(skill))
Player.UnlockedSkills.Add(skill);
}
Session.SkillsUnlocked.Clear();
}
/// <summary>
/// Gets all unlocked skills from player persistence and current session.
/// </summary>
public List<SkillData> GetAllUnlockedSkills()
{
var result = new List<SkillData>(Player.UnlockedSkills);
foreach (var skill in Session.SkillsUnlocked)
{
if (!result.Contains(skill)) result.Add(skill);
}
return result;
}
#endregion
#region Reset Operations
/// <summary>
/// Resets only the session state.
/// </summary>
public void ResetSession() => Session.Reset();
/// <summary>
/// Resets everything to defaults.
/// </summary>
public void ResetAll()
{
Player.Reset();
Session.ResetAll();
}
#endregion
}

View File

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

View File

@@ -1,61 +1,192 @@
using System.Collections.Generic;
using System.Text.Json;
using Godot;
using Godot.Collections;
using Mr.BrickAdventures.scripts.Resources;
using Mr.BrickAdventures.scripts.State;
namespace Mr.BrickAdventures.Autoloads;
/// <summary>
/// Save system that serializes state to JSON using DTOs.
/// </summary>
public partial class SaveSystem : Node
{
[Export] public string SavePath { get; set; } = "user://savegame.save";
[Export] public int Version { get; set; } = 1;
[Export] public string SavePath { get; set; } = "user://savegame.json";
[Export] public int Version { get; set; } = 2;
private GameManager _gameManager;
public static SaveSystem Instance { get; private set; }
private static readonly JsonSerializerOptions JsonOptions = new()
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
public override void _Ready()
{
_gameManager = GetNode<GameManager>("/root/GameManager");
Instance = this;
}
public override void _ExitTree()
{
if (Instance == this) Instance = null;
}
public void SaveGame()
{
var saveData = new Dictionary
var store = GameStateStore.Instance;
if (store == null)
{
{ "player_state", _gameManager.PlayerState},
{ "version", Version}
GD.PrintErr("SaveSystem: GameStateStore not available.");
return;
}
// Convert to DTO (only serializable data)
var saveData = new SaveDataDto
{
Version = Version,
Coins = store.Player.Coins,
Lives = store.Player.Lives,
CurrentLevel = store.Session.CurrentLevel,
CompletedLevels = [.. store.Player.CompletedLevels],
UnlockedLevels = new List<int>(store.Player.UnlockedLevels),
UnlockedSkillNames = GetSkillNames(store.Player.UnlockedSkills),
UnlockedAchievements = new List<string>(store.Player.UnlockedAchievements),
Statistics = new Dictionary<string, int>(store.Player.Statistics)
};
try
{
var json = JsonSerializer.Serialize(saveData, JsonOptions);
using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Write);
file.StoreVar(saveData);
GD.Print("Game state saved to: ", SavePath);
file.StoreString(json);
GD.Print("Game saved to: ", SavePath);
EventBus.EmitGameSaved();
}
catch (System.Exception e)
{
GD.PrintErr($"SaveSystem: Failed to save game: {e.Message}");
}
}
public bool LoadGame()
{
if (!FileAccess.FileExists(SavePath))
return false;
using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Read);
var saveDataObj = (Dictionary)file.GetVar();
if (saveDataObj.ContainsKey("version") && (int)saveDataObj["version"] != Version)
{
GD.Print($"Save file version mismatch. Expected: {Version}, Found: {saveDataObj["version"]}");
GD.Print("SaveSystem: No save file found.");
return false;
}
GD.Print("Game state loaded from: ", SavePath);
GD.Print("Player state: ", saveDataObj["player_state"]);
_gameManager.PlayerState = (Dictionary)saveDataObj["player_state"];
try
{
using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Read);
var json = file.GetAsText();
var saveData = JsonSerializer.Deserialize<SaveDataDto>(json, JsonOptions);
var skills = new Array<SkillData>();
foreach (var skill in (Array<SkillData>)_gameManager.PlayerState["unlocked_skills"])
if (saveData == null)
{
GD.PrintErr("SaveSystem: Failed to deserialize save data.");
return false;
}
if (saveData.Version != Version)
{
GD.PrintErr($"SaveSystem: Version mismatch. Expected {Version}, found {saveData.Version}");
return false;
}
var store = GameStateStore.Instance;
if (store == null)
{
GD.PrintErr("SaveSystem: GameStateStore not available.");
return false;
}
// Apply loaded state
store.Player.Coins = saveData.Coins;
store.Player.Lives = saveData.Lives;
store.Session.CurrentLevel = saveData.CurrentLevel;
store.Player.CompletedLevels = saveData.CompletedLevels ?? new List<int>();
store.Player.UnlockedLevels = saveData.UnlockedLevels ?? new List<int> { 0 };
store.Player.UnlockedAchievements = saveData.UnlockedAchievements ?? new List<string>();
store.Player.Statistics = saveData.Statistics ?? new Dictionary<string, int>();
// Reload skills by name from SkillManager
store.Player.UnlockedSkills = LoadSkillsByName(saveData.UnlockedSkillNames);
GD.Print("Game loaded from: ", SavePath);
return true;
}
catch (System.Exception e)
{
GD.PrintErr($"SaveSystem: Failed to load game: {e.Message}");
return false;
}
}
private static List<string> GetSkillNames(List<SkillData> skills)
{
var names = new List<string>();
foreach (var skill in skills)
{
if (skill != null)
names.Add(skill.Name);
}
return names;
}
private List<SkillData> LoadSkillsByName(List<string> skillNames)
{
var skills = new List<SkillData>();
if (skillNames == null) return skills;
var skillManager = GetNodeOrNull<SkillManager>(Constants.SkillManagerPath);
if (skillManager == null)
{
GD.PrintErr("SaveSystem: SkillManager not available to resolve skill names.");
return skills;
}
foreach (var name in skillNames)
{
var skill = skillManager.GetSkillByName(name);
if (skill != null)
{
skills.Add(skill);
}
_gameManager.UnlockSkills(skills);
return true;
else
{
GD.PrintErr($"SaveSystem: Skill '{name}' not found in SkillManager.");
}
}
return skills;
}
public bool CheckSaveExists() => FileAccess.FileExists(SavePath);
public void DeleteSave()
{
if (FileAccess.FileExists(SavePath))
{
DirAccess.RemoveAbsolute(ProjectSettings.GlobalizePath(SavePath));
GD.Print("Save file deleted.");
}
}
}
/// <summary>
/// Serializable DTO for save data - no Godot types.
/// </summary>
public class SaveDataDto
{
public int Version { get; set; }
public int Coins { get; set; }
public int Lives { get; set; }
public int CurrentLevel { get; set; }
public List<int> CompletedLevels { get; set; }
public List<int> UnlockedLevels { get; set; }
public List<string> UnlockedSkillNames { get; set; }
public List<string> UnlockedAchievements { get; set; }
public Dictionary<string, int> Statistics { get; set; }
}

View File

@@ -2,6 +2,7 @@ using System.Collections.Generic;
using System.Linq;
using Godot;
using Godot.Collections;
using Mr.BrickAdventures;
using Mr.BrickAdventures.scripts.components;
using Mr.BrickAdventures.scripts.interfaces;
using Mr.BrickAdventures.scripts.Resources;
@@ -17,6 +18,8 @@ public partial class SkillManager : Node
public Dictionary ActiveComponents { get; private set; } = new();
public static SkillManager Instance { get; private set; }
[Signal]
public delegate void ActiveThrowSkillChangedEventHandler(BrickThrowComponent throwComponent);
[Signal]
@@ -24,7 +27,13 @@ public partial class SkillManager : Node
public override void _Ready()
{
_gameManager = GetNode<GameManager>("/root/GameManager");
Instance = this;
_gameManager = GetNode<GameManager>(Constants.GameManagerPath);
}
public override void _ExitTree()
{
if (Instance == this) Instance = null;
}
/// <summary>
@@ -142,7 +151,6 @@ public partial class SkillManager : Node
{
if (s.Name == skillName)
{
s.IsActive = false;
break;
}
}
@@ -187,12 +195,16 @@ public partial class SkillManager : Node
return null;
}
public bool IsSkillActive(SkillData skill)
{
return skill != null && ActiveComponents.ContainsKey(skill.Name);
}
public void ActivateSkill(SkillData skill)
{
if (!ActiveComponents.ContainsKey(skill.Name))
{
AddSkill(skill);
skill.IsActive = true;
}
}
@@ -201,7 +213,6 @@ public partial class SkillManager : Node
if (ActiveComponents.ContainsKey(skill.Name))
{
RemoveSkill(skill.Name);
skill.IsActive = false;
}
}

View File

@@ -1,32 +1,21 @@
using System.Collections.Generic;
using Godot;
using Godot.Collections;
using Mr.BrickAdventures.scripts.State;
namespace Mr.BrickAdventures.Autoloads;
/// <summary>
/// Manages game statistics using GameStateStore.
/// </summary>
public partial class StatisticsManager : Node
{
private GameManager _gameManager;
private AchievementManager _achievementManager;
private Dictionary<string, Variant> _stats = new();
public override void _Ready()
/// <summary>
/// Gets the statistics dictionary from the store.
/// </summary>
private Dictionary<string, int> GetStats()
{
_gameManager = GetNode<GameManager>("/root/GameManager");
_achievementManager = GetNode<AchievementManager>("/root/AchievementManager");
LoadStatistics();
}
private void LoadStatistics()
{
if (_gameManager.PlayerState.TryGetValue("statistics", out var statsObj))
{
_stats = (Dictionary<string, Variant>)statsObj;
}
else
{
_stats = new Dictionary<string, Variant>();
_gameManager.PlayerState["statistics"] = _stats;
}
return GameStateStore.Instance?.Player.Statistics ?? new Dictionary<string, int>();
}
/// <summary>
@@ -34,45 +23,40 @@ public partial class StatisticsManager : Node
/// </summary>
public void IncrementStat(string statName, int amount = 1)
{
if (_stats.TryGetValue(statName, out var currentValue))
var stats = GetStats();
if (stats.TryGetValue(statName, out var currentValue))
{
_stats[statName] = (int)currentValue + amount;
stats[statName] = currentValue + amount;
}
else
{
_stats[statName] = amount;
stats[statName] = amount;
}
GD.Print($"Stat '{statName}' updated to: {_stats[statName]}");
CheckAchievementsForStat(statName);
}
/// <summary>
/// Sets a statistic to a specific value.
/// </summary>
public void SetStat(string statName, int value)
{
var stats = GetStats();
stats[statName] = value;
}
/// <summary>
/// Gets the value of a statistic.
/// </summary>
public Variant GetStat(string statName, Variant defaultValue = default)
public int GetStat(string statName)
{
return _stats.TryGetValue(statName, out var value) ? value : defaultValue;
var stats = GetStats();
return stats.TryGetValue(statName, out var value) ? value : 0;
}
/// <summary>
/// Checks if the updated stat meets the criteria for any achievements.
/// Gets a copy of all statistics.
/// </summary>
private void CheckAchievementsForStat(string statName)
public Dictionary<string, int> GetAllStats()
{
switch (statName)
{
case "enemies_defeated":
if ((int)GetStat(statName, 0) >= 100)
{
_achievementManager.UnlockAchievement("slayer_100_enemies");
}
break;
case "jumps_made":
if ((int)GetStat(statName, 0) >= 1000)
{
_achievementManager.UnlockAchievement("super_jumper");
}
break;
}
return new Dictionary<string, int>(GetStats());
}
}

View File

@@ -1,4 +1,4 @@
<Project Sdk="Godot.NET.Sdk/4.4.1">
<Project Sdk="Godot.NET.Sdk/4.6.0">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<EnableDynamicLoading>true</EnableDynamicLoading>
@@ -8,6 +8,5 @@
<PackageReference Include="Facepunch.Steamworks" Version="2.3.3" />
<PackageReference Include="Facepunch.Steamworks.Dlls" Version="2.3.2" />
<PackageReference Include="Facepunch.Steamworks.Library" Version="2.3.3" />
<PackageReference Include="LimboConsole.Sharp" Version="0.0.1-beta-008" />
</ItemGroup>
</Project>

View File

@@ -1,4 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AArea2D_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FSourcesCache_003F116d8c5f8dae51522ba398e1d89e3d4722f4af7b6e7f071196b928be44af7_003FArea2D_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACamera2D_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fa2e12a1a67ad701a97608de6be85250e3e353951ecf8058a02c703490c753_003FCamera2D_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACanvasItem_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003Fef7b819b226fab796d1dfe66d415dd7510bcac87675020ddb8f03a828e763_003FCanvasItem_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACecovym_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003Ftmp_003FJetBrainsPerUserTemp_002D1000_002D1_003FSandboxFiles_003FSadijuw_003FCecovym_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
@@ -10,5 +11,7 @@
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AList_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fe747192abb38e2df82cbdb37e721567726f559914a7b81f8b26ba537de632f4_003FList_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMr_002EBrickAdventures_002Escripts_002Ecomponents_002ECollectableComponent_005FScriptSignals_002Egenerated_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FSourcesCache_003F80d9408eb7280c15eb4a12b61cdf8f7f1b0c5a2_003FMr_002EBrickAdventures_002Escripts_002Ecomponents_002ECollectableComponent_005FScriptSignals_002Egenerated_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANode2D_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F86db9cd834346aad02d74c1b66dd9c64d6ef3147435dd9c9c9477b48f7_003FNode2D_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APhysicsBody2D_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FSourcesCache_003F4fcd28ddaffa68e6fe53288161b788dea7d402b4a41b9d9f0f0f2c52f9af075_003FPhysicsBody2D_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ARectangleShape2D_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003Fa1cc98873548652da0c14ecefa4737431426fcbb24a7f0641e3d9c266c3_003FRectangleShape2D_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ARigidBody2D_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E2_003Fresharper_002Dhost_003FSourcesCache_003F7bc3ccc1ac5bbc68933d64c7b7eb5ab4aecde2b73c686dd6495b68bdf08ba5b2_003FRigidBody2D_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AShape2D_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F3671dbbd9b17cdf2bf9075b468b6bd7e3ab13fc3be7a116484085d3b6cc9fe_003FShape2D_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=7 format=3 uid="uid://bargnp4twtmxg"]
[gd_scene format=3 uid="uid://bargnp4twtmxg"]
[ext_resource type="Texture2D" uid="uid://djifxc5x0dyrw" path="res://sprites/ppc_tileset.png" id="1_aya2w"]
[ext_resource type="Script" uid="uid://r4jybneigfcn" path="res://scripts/components/CollectableComponent.cs" id="2_htmrw"]
@@ -9,33 +9,33 @@
[sub_resource type="CircleShape2D" id="CircleShape2D_3ask2"]
radius = 9.0
[node name="Big Coin" type="Area2D" groups=["coins"]]
[node name="Big Coin" type="Area2D" unique_id=354254828 groups=["coins"]]
scale = Vector2(2, 2)
collision_layer = 2
collision_mask = 4
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
[node name="CollisionShape2D" type="CollisionShape2D" parent="." unique_id=2066526597]
shape = SubResource("CircleShape2D_3ask2")
[node name="Sprite2D" type="Sprite2D" parent="."]
[node name="Sprite2D" type="Sprite2D" parent="." unique_id=1626463721]
texture = ExtResource("1_aya2w")
hframes = 12
vframes = 12
frame = 51
[node name="CollectableComponent" type="Node" parent="." node_paths=PackedStringArray("Area2D", "CollisionShape", "Sfx")]
[node name="CollectableComponent" type="Node" parent="." unique_id=1124310030 node_paths=PackedStringArray("Area2D", "CollisionShape", "Sfx")]
script = ExtResource("2_htmrw")
Area2D = NodePath("..")
CollisionShape = NodePath("../CollisionShape2D")
Data = ExtResource("3_lk3av")
Sfx = NodePath("../sfx")
[node name="FadeAwayComponent" type="Node" parent="." node_paths=PackedStringArray("Sprite", "Area")]
[node name="FadeAwayComponent" type="Node" parent="." unique_id=222617739 node_paths=PackedStringArray("Sprite", "Area")]
script = ExtResource("4_62p7g")
Sprite = NodePath("../Sprite2D")
Area = NodePath("..")
[node name="sfx" type="AudioStreamPlayer2D" parent="."]
[node name="sfx" type="AudioStreamPlayer2D" parent="." unique_id=1978747299]
stream = ExtResource("5_dbffd")
volume_db = -5.0
bus = &"sfx"

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=7 format=3 uid="uid://d08dfqmirnd66"]
[gd_scene format=3 uid="uid://d08dfqmirnd66"]
[ext_resource type="Texture2D" uid="uid://djifxc5x0dyrw" path="res://sprites/ppc_tileset.png" id="1_1co1x"]
[ext_resource type="Script" uid="uid://r4jybneigfcn" path="res://scripts/components/CollectableComponent.cs" id="2_lthbn"]
@@ -9,32 +9,32 @@
[sub_resource type="CircleShape2D" id="CircleShape2D_3ask2"]
radius = 9.0
[node name="Big Treasure" type="Area2D" groups=["coins"]]
[node name="Big Treasure" type="Area2D" unique_id=1021217632 groups=["coins"]]
collision_layer = 2
collision_mask = 4
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
[node name="CollisionShape2D" type="CollisionShape2D" parent="." unique_id=458290011]
shape = SubResource("CircleShape2D_3ask2")
[node name="Sprite2D" type="Sprite2D" parent="."]
[node name="Sprite2D" type="Sprite2D" parent="." unique_id=564060301]
texture = ExtResource("1_1co1x")
hframes = 12
vframes = 12
frame = 64
[node name="CollectableComponent" type="Node" parent="." node_paths=PackedStringArray("Area2D", "CollisionShape", "Sfx")]
[node name="CollectableComponent" type="Node" parent="." unique_id=2009195182 node_paths=PackedStringArray("Area2D", "CollisionShape", "Sfx")]
script = ExtResource("2_lthbn")
Area2D = NodePath("..")
CollisionShape = NodePath("../CollisionShape2D")
Data = ExtResource("3_k64cr")
Sfx = NodePath("../sfx")
[node name="FadeAwayComponent" type="Node" parent="." node_paths=PackedStringArray("Sprite", "Area")]
[node name="FadeAwayComponent" type="Node" parent="." unique_id=1809580000 node_paths=PackedStringArray("Sprite", "Area")]
script = ExtResource("4_qwwsj")
Sprite = NodePath("../Sprite2D")
FadeDuration = 0.4
Area = NodePath("..")
[node name="sfx" type="AudioStreamPlayer2D" parent="."]
[node name="sfx" type="AudioStreamPlayer2D" parent="." unique_id=466669009]
stream = ExtResource("5_fxf8v")
bus = &"sfx"

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=58 format=3 uid="uid://bqi5s710xb1ju"]
[gd_scene load_steps=57 format=3 uid="uid://bqi5s710xb1ju"]
[ext_resource type="Script" uid="uid://csel4s0e4g5uf" path="res://scripts/components/PlayerController.cs" id="1_yysbb"]
[ext_resource type="Shader" uid="uid://bs4xvm4qkurpr" path="res://shaders/hit_flash.tres" id="2_lgb3u"]
@@ -19,7 +19,6 @@
[ext_resource type="PackedScene" uid="uid://dre1vit1m4d2n" path="res://objects/movement_abilities/grid_movement_ability.tscn" id="8_xuhvf"]
[ext_resource type="Script" uid="uid://dy78ak8eykw6e" path="res://scripts/components/FlipComponent.cs" id="9_yysbb"]
[ext_resource type="Script" uid="uid://mnjg3p0aw1ow" path="res://scripts/components/CanPickUpComponent.cs" id="10_yysbb"]
[ext_resource type="Script" uid="uid://ccqb8kd5m0eh7" path="res://scripts/components/ScoreComponent.cs" id="11_o1ihh"]
[ext_resource type="Script" uid="uid://dgb8bqcri7nsj" path="res://scripts/components/HealthComponent.cs" id="12_ur2y5"]
[ext_resource type="Script" uid="uid://byw1legrv1ep2" path="res://scripts/components/PlayerDeathComponent.cs" id="13_7til7"]
[ext_resource type="Script" uid="uid://cecelixl41t3j" path="res://scripts/components/InvulnerabilityComponent.cs" id="15_xuhvf"]
@@ -175,9 +174,6 @@ shape = SubResource("RectangleShape2D_vad0t")
[node name="CanPickUpComponent" type="Node" parent="."]
script = ExtResource("10_yysbb")
[node name="ScoreComponent" type="Node" parent="."]
script = ExtResource("11_o1ihh")
[node name="HealthComponent" type="Node2D" parent="." node_paths=PackedStringArray("HurtSfx", "HealSfx")]
script = ExtResource("12_ur2y5")
HurtSfx = NodePath("../sfx_hurt")

View File

@@ -15,7 +15,7 @@ radius = 4.0
[node name="Bullet" type="Area2D"]
collision_layer = 64
collision_mask = 21
collision_mask = 85
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
shape = SubResource("CircleShape2D_txsw8")
@@ -47,7 +47,7 @@ VisibilityNotifier = NodePath("../VisibleOnScreenNotifier2D")
[node name="LifetimeComponent" type="Node" parent="."]
script = ExtResource("4_aniyw")
LifeTime = 10.0
LifeTime = 3.0
[node name="LaunchComponent" type="Node2D" parent="."]
script = ExtResource("5_3ks47")

View File

@@ -0,0 +1,48 @@
[gd_scene load_steps=7 format=3 uid="uid://qrbiu1qo4tt5"]
[ext_resource type="Texture2D" uid="uid://djifxc5x0dyrw" path="res://sprites/ppc_tileset.png" id="1_0efvn"]
[ext_resource type="Script" uid="uid://bnaxy8cw3wrko" path="res://scripts/components/PeriodicShootingComponent.cs" id="2_4ycs1"]
[ext_resource type="PackedScene" uid="uid://chetx6gmnwbxi" path="res://objects/entities/cannon_bullet.tscn" id="3_ab2uh"]
[ext_resource type="Script" uid="uid://b3j23e7b7x8ro" path="res://scripts/components/RecoilComponent.cs" id="4_tynaf"]
[ext_resource type="Script" uid="uid://c707c53k7c5ae" path="res://scripts/components/SquashAndStretchComponent.cs" id="5_0xigu"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_j5sus"]
size = Vector2(16, 16)
[node name="Cannon" type="StaticBody2D"]
collision_layer = 0
collision_mask = 0
[node name="Sprite2D" type="Sprite2D" parent="."]
rotation = 3.14159
texture = ExtResource("1_0efvn")
hframes = 12
vframes = 12
frame = 42
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
shape = SubResource("RectangleShape2D_j5sus")
[node name="PeriodicShootingComponent" type="Node" parent="." node_paths=PackedStringArray("BulletSpawnPointRight")]
script = ExtResource("2_4ycs1")
BulletScene = ExtResource("3_ab2uh")
ShootInterval = 3.0
ShootDirection = Vector2(0, 1)
BulletSpawnPointRight = NodePath("../Bullet spawn")
ShootingIntervalVariation = 0.61
metadata/_custom_type_script = "uid://bnaxy8cw3wrko"
[node name="Bullet spawn" type="Marker2D" parent="."]
position = Vector2(0, 12)
[node name="RecoilComponent" type="Node" parent="." node_paths=PackedStringArray("RecoilTarget")]
script = ExtResource("4_tynaf")
RecoilTarget = NodePath("../Sprite2D")
RecoilDistance = 4.0
RecoilDuration = 0.12
metadata/_custom_type_script = "uid://b3j23e7b7x8ro"
[node name="SquashAndStretchComponent" type="Node" parent="." node_paths=PackedStringArray("TargetNode")]
script = ExtResource("5_0xigu")
TargetNode = NodePath("../Sprite2D")
metadata/_custom_type_script = "uid://c707c53k7c5ae"

View File

@@ -0,0 +1,48 @@
[gd_scene load_steps=7 format=3 uid="uid://cm0rd70wafif1"]
[ext_resource type="Texture2D" uid="uid://djifxc5x0dyrw" path="res://sprites/ppc_tileset.png" id="1_hnnrt"]
[ext_resource type="Script" uid="uid://bnaxy8cw3wrko" path="res://scripts/components/PeriodicShootingComponent.cs" id="2_cho7n"]
[ext_resource type="PackedScene" uid="uid://chetx6gmnwbxi" path="res://objects/entities/cannon_bullet.tscn" id="3_kgmtj"]
[ext_resource type="Script" uid="uid://b3j23e7b7x8ro" path="res://scripts/components/RecoilComponent.cs" id="4_nfk87"]
[ext_resource type="Script" uid="uid://c707c53k7c5ae" path="res://scripts/components/SquashAndStretchComponent.cs" id="5_43fqe"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_j5sus"]
size = Vector2(16, 16)
[node name="Cannon" type="StaticBody2D"]
collision_layer = 0
collision_mask = 0
[node name="Sprite2D" type="Sprite2D" parent="."]
rotation = -1.5708
texture = ExtResource("1_hnnrt")
hframes = 12
vframes = 12
frame = 42
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
shape = SubResource("RectangleShape2D_j5sus")
[node name="PeriodicShootingComponent" type="Node" parent="." node_paths=PackedStringArray("BulletSpawnPointRight")]
script = ExtResource("2_cho7n")
BulletScene = ExtResource("3_kgmtj")
ShootInterval = 3.0
ShootDirection = Vector2(-1, 0)
BulletSpawnPointRight = NodePath("../Bullet spawn")
ShootingIntervalVariation = 0.61
metadata/_custom_type_script = "uid://bnaxy8cw3wrko"
[node name="Bullet spawn" type="Marker2D" parent="."]
position = Vector2(-12, 0)
[node name="RecoilComponent" type="Node" parent="." node_paths=PackedStringArray("RecoilTarget")]
script = ExtResource("4_nfk87")
RecoilTarget = NodePath("../Sprite2D")
RecoilDistance = 4.0
RecoilDuration = 0.12
metadata/_custom_type_script = "uid://b3j23e7b7x8ro"
[node name="SquashAndStretchComponent" type="Node" parent="." node_paths=PackedStringArray("TargetNode")]
script = ExtResource("5_43fqe")
TargetNode = NodePath("../Sprite2D")
metadata/_custom_type_script = "uid://c707c53k7c5ae"

View File

@@ -0,0 +1,47 @@
[gd_scene load_steps=7 format=3 uid="uid://dr6srln4mckwn"]
[ext_resource type="Texture2D" uid="uid://djifxc5x0dyrw" path="res://sprites/ppc_tileset.png" id="1_r5bjs"]
[ext_resource type="Script" uid="uid://bnaxy8cw3wrko" path="res://scripts/components/PeriodicShootingComponent.cs" id="2_gd3be"]
[ext_resource type="PackedScene" uid="uid://chetx6gmnwbxi" path="res://objects/entities/cannon_bullet.tscn" id="3_1gufj"]
[ext_resource type="Script" uid="uid://b3j23e7b7x8ro" path="res://scripts/components/RecoilComponent.cs" id="4_hwc6x"]
[ext_resource type="Script" uid="uid://c707c53k7c5ae" path="res://scripts/components/SquashAndStretchComponent.cs" id="5_o674s"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_j5sus"]
size = Vector2(16, 16)
[node name="Cannon" type="StaticBody2D"]
collision_layer = 0
collision_mask = 0
[node name="Sprite2D" type="Sprite2D" parent="."]
rotation = 1.5708
texture = ExtResource("1_r5bjs")
hframes = 12
vframes = 12
frame = 42
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
shape = SubResource("RectangleShape2D_j5sus")
[node name="PeriodicShootingComponent" type="Node" parent="." node_paths=PackedStringArray("BulletSpawnPointRight")]
script = ExtResource("2_gd3be")
BulletScene = ExtResource("3_1gufj")
ShootInterval = 3.0
BulletSpawnPointRight = NodePath("../Bullet spawn")
ShootingIntervalVariation = 0.61
metadata/_custom_type_script = "uid://bnaxy8cw3wrko"
[node name="Bullet spawn" type="Marker2D" parent="."]
position = Vector2(12, 0)
[node name="RecoilComponent" type="Node" parent="." node_paths=PackedStringArray("RecoilTarget")]
script = ExtResource("4_hwc6x")
RecoilTarget = NodePath("../Sprite2D")
RecoilDistance = 4.0
RecoilDuration = 0.12
metadata/_custom_type_script = "uid://b3j23e7b7x8ro"
[node name="SquashAndStretchComponent" type="Node" parent="." node_paths=PackedStringArray("TargetNode")]
script = ExtResource("5_o674s")
TargetNode = NodePath("../Sprite2D")
metadata/_custom_type_script = "uid://c707c53k7c5ae"

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=7 format=3 uid="uid://54w4wisfj8v8"]
[gd_scene format=3 uid="uid://54w4wisfj8v8"]
[ext_resource type="Texture2D" uid="uid://djifxc5x0dyrw" path="res://sprites/ppc_tileset.png" id="1_epuj5"]
[ext_resource type="Script" uid="uid://r4jybneigfcn" path="res://scripts/components/CollectableComponent.cs" id="2_gxix7"]
@@ -9,33 +9,33 @@
[sub_resource type="CircleShape2D" id="CircleShape2D_3ask2"]
radius = 9.0
[node name="Coin" type="Area2D" groups=["coins"]]
[node name="Coin" type="Area2D" unique_id=1771447403 groups=["coins"]]
collision_layer = 2
collision_mask = 4
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
[node name="CollisionShape2D" type="CollisionShape2D" parent="." unique_id=707378099]
shape = SubResource("CircleShape2D_3ask2")
[node name="Sprite2D" type="Sprite2D" parent="."]
[node name="Sprite2D" type="Sprite2D" parent="." unique_id=898624458]
texture = ExtResource("1_epuj5")
hframes = 12
vframes = 12
frame = 51
[node name="CollectableComponent" type="Node" parent="." node_paths=PackedStringArray("Area2D", "CollisionShape", "Sfx")]
[node name="CollectableComponent" type="Node" parent="." unique_id=564726971 node_paths=PackedStringArray("Area2D", "CollisionShape", "Sfx")]
script = ExtResource("2_gxix7")
Area2D = NodePath("..")
CollisionShape = NodePath("../CollisionShape2D")
Data = ExtResource("3_fm2fq")
Sfx = NodePath("../sfx")
[node name="FadeAwayComponent" type="Node" parent="." node_paths=PackedStringArray("Sprite", "Area")]
[node name="FadeAwayComponent" type="Node" parent="." unique_id=1534239994 node_paths=PackedStringArray("Sprite", "Area")]
script = ExtResource("4_gxix7")
Sprite = NodePath("../Sprite2D")
FadeDuration = 0.4
Area = NodePath("..")
[node name="sfx" type="AudioStreamPlayer2D" parent="."]
[node name="sfx" type="AudioStreamPlayer2D" parent="." unique_id=1641717]
stream = ExtResource("5_4jc2c")
volume_db = -10.0
bus = &"sfx"

View File

@@ -0,0 +1,34 @@
[gd_scene load_steps=6 format=3 uid="uid://dk2cu8qs7odib"]
[ext_resource type="Texture2D" uid="uid://djifxc5x0dyrw" path="res://sprites/ppc_tileset.png" id="1_214vd"]
[ext_resource type="Script" uid="uid://r4jybneigfcn" path="res://scripts/components/CollectableComponent.cs" id="2_h7fi3"]
[ext_resource type="Script" uid="uid://bjln6jb1sigx2" path="res://scripts/components/FadeAwayComponent.cs" id="3_b687r"]
[ext_resource type="Resource" path="res://resources/collectables/double_jump_pickup.tres" id="3_h7fi3"]
[sub_resource type="CircleShape2D" id="CircleShape2D_pickup"]
radius = 12.0
[node name="SkillPickup" type="Area2D" groups=["Collectables"]]
collision_layer = 2
collision_mask = 4
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
shape = SubResource("CircleShape2D_pickup")
[node name="Sprite2D" type="Sprite2D" parent="."]
texture = ExtResource("1_214vd")
hframes = 12
vframes = 12
frame = 24
[node name="CollectableComponent" type="Node" parent="." node_paths=PackedStringArray("Area2D", "CollisionShape")]
script = ExtResource("2_h7fi3")
Area2D = NodePath("..")
CollisionShape = NodePath("../CollisionShape2D")
Data = ExtResource("3_h7fi3")
[node name="FadeAwayComponent" type="Node" parent="." node_paths=PackedStringArray("Sprite", "Area")]
script = ExtResource("3_b687r")
Sprite = NodePath("../Sprite2D")
FadeDuration = 0.5
Area = NodePath("..")

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=6 format=3 uid="uid://12jnkdygpxwc"]
[gd_scene format=3 uid="uid://12jnkdygpxwc"]
[ext_resource type="Texture2D" uid="uid://djifxc5x0dyrw" path="res://sprites/ppc_tileset.png" id="1_agxwm"]
[ext_resource type="Script" uid="uid://dnh0mekg2vqxi" path="res://scripts/components/RequirementComponent.cs" id="2_ed7mh"]
@@ -8,14 +8,14 @@
[sub_resource type="RectangleShape2D" id="RectangleShape2D_yfu6m"]
size = Vector2(28, 32)
[node name="ExitLevel" type="Area2D" node_paths=PackedStringArray("DoorSprite")]
[node name="ExitLevel" type="Area2D" unique_id=1951927125 node_paths=PackedStringArray("DoorSprite")]
collision_layer = 0
collision_mask = 4
script = ExtResource("4_06sog")
DoorSprite = NodePath("Sprite2D")
OpenedDoorFrame = 88
[node name="Sprite2D" type="Sprite2D" parent="."]
[node name="Sprite2D" type="Sprite2D" parent="." unique_id=1296410089]
position = Vector2(0, -8)
scale = Vector2(2, 2)
texture = ExtResource("1_agxwm")
@@ -23,15 +23,15 @@ hframes = 12
vframes = 12
frame = 54
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
[node name="CollisionShape2D" type="CollisionShape2D" parent="." unique_id=220927363]
position = Vector2(0, -8)
shape = SubResource("RectangleShape2D_yfu6m")
[node name="RequirementComponent" type="Node" parent="."]
[node name="RequirementComponent" type="Node" parent="." unique_id=1338124097]
script = ExtResource("2_ed7mh")
RequirementType = 1
RequirementType = 2
[node name="UnlockOnRequirementComponent" type="Node" parent="." node_paths=PackedStringArray("RequirementComponent", "UnlockTarget")]
[node name="UnlockOnRequirementComponent" type="Node" parent="." unique_id=1279359200 node_paths=PackedStringArray("RequirementComponent", "UnlockTarget")]
script = ExtResource("3_ed7mh")
RequirementComponent = NodePath("../RequirementComponent")
UnlockTarget = NodePath("..")

View File

@@ -0,0 +1,78 @@
[gd_scene load_steps=11 format=3 uid="uid://bmk3ddwv33dud"]
[ext_resource type="Texture2D" uid="uid://ccjihsk6w8sow" path="res://sprites/BFT - Mega Metroidvania Tileset.png" id="1_erbp6"]
[ext_resource type="Script" uid="uid://2i7p7v135u7c" path="res://scripts/components/DamageComponent.cs" id="2_fq2gn"]
[ext_resource type="Script" uid="uid://v7tt4w6bejux" path="res://scripts/components/CleanupComponent.cs" id="4_mgh1e"]
[ext_resource type="Script" uid="uid://nhu2xd8611fk" path="res://scripts/components/HazardComponent.cs" id="5_8g254"]
[ext_resource type="Script" uid="uid://chusyr5vwgwf0" path="res://scripts/components/CleanupOnCollisionComponent.cs" id="6_2rpon"]
[ext_resource type="Script" uid="uid://cgfynrn68lp12" path="res://scripts/components/KnockbackComponent.cs" id="6_3n0l8"]
[ext_resource type="Script" uid="uid://co05ugnvx0v3e" path="res://scripts/components/FallOnDetectionComponent.cs" id="7_mgh1e"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_t3a48"]
size = Vector2(16, 8)
[sub_resource type="RectangleShape2D" id="RectangleShape2D_fq2gn"]
size = Vector2(16, 320)
[sub_resource type="RectangleShape2D" id="RectangleShape2D_2rpon"]
size = Vector2(17, 9)
[node name="FallingSpike" type="RigidBody2D"]
collision_mask = 5
gravity_scale = 0.0
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
position = Vector2(0, -4)
shape = SubResource("RectangleShape2D_t3a48")
[node name="Sprite2D" type="Sprite2D" parent="."]
rotation = -3.14159
texture = ExtResource("1_erbp6")
hframes = 13
vframes = 45
frame = 9
[node name="DamageComponent" type="Node" parent="." node_paths=PackedStringArray("Area")]
script = ExtResource("2_fq2gn")
Area = NodePath("")
metadata/_custom_type_script = "uid://2i7p7v135u7c"
[node name="HazardComponent" type="Node2D" parent="." node_paths=PackedStringArray("KnockbackComponent", "HazardArea")]
script = ExtResource("5_8g254")
KnockbackComponent = NodePath("../KnockbackComponent")
HazardArea = NodePath("../HazardArea")
metadata/_custom_type_script = "uid://nhu2xd8611fk"
[node name="KnockbackComponent" type="Node" parent="."]
script = ExtResource("6_3n0l8")
metadata/_custom_type_script = "uid://cgfynrn68lp12"
[node name="FallTriggerArea" type="Area2D" parent="."]
collision_mask = 4
[node name="CollisionShape2D" type="CollisionShape2D" parent="FallTriggerArea"]
position = Vector2(0, 155)
shape = SubResource("RectangleShape2D_fq2gn")
debug_color = Color(0.916282, 7.47952e-05, 0.709809, 0.42)
[node name="CleanupComponent" type="Node" parent="."]
script = ExtResource("4_mgh1e")
metadata/_custom_type_script = "uid://v7tt4w6bejux"
[node name="CleanupOnCollisionComponent" type="Node" parent="."]
script = ExtResource("6_2rpon")
metadata/_custom_type_script = "uid://chusyr5vwgwf0"
[node name="FallOnDetectionComponent" type="Node2D" parent="." node_paths=PackedStringArray("DetectionArea", "TargetBody")]
script = ExtResource("7_mgh1e")
DetectionArea = NodePath("../FallTriggerArea")
TargetBody = NodePath("..")
metadata/_custom_type_script = "uid://co05ugnvx0v3e"
[node name="HazardArea" type="Area2D" parent="."]
collision_mask = 4
[node name="CollisionShape2D" type="CollisionShape2D" parent="HazardArea"]
position = Vector2(0, -4.5)
shape = SubResource("RectangleShape2D_2rpon")
debug_color = Color(0.73011, 0.468379, 0.188355, 0.42)

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=30 format=3 uid="uid://xp4njljog0x2"]
[gd_scene format=3 uid="uid://xp4njljog0x2"]
[ext_resource type="Texture2D" uid="uid://22k1u37j6k8y" path="res://sprites/flying_enemy.png" id="1_30hhw"]
[ext_resource type="Shader" uid="uid://bs4xvm4qkurpr" path="res://shaders/hit_flash.tres" id="1_uyhuj"]
@@ -60,7 +60,7 @@ point_count = 2
[sub_resource type="CurveTexture" id="CurveTexture_7b7mt"]
curve = SubResource("Curve_f2w8b")
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_qxfb0"]
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_q78ru"]
resource_local_to_scene = true
lifetime_randomness = 1.0
particle_flag_disable_z = true
@@ -80,50 +80,52 @@ color_ramp = SubResource("GradientTexture1D_f1fvy")
[sub_resource type="RectangleShape2D" id="RectangleShape2D_cmp1h"]
size = Vector2(16, 26)
[node name="Flying Enemy" type="CharacterBody2D"]
[node name="Flying Enemy" type="CharacterBody2D" unique_id=1068972930]
collision_layer = 8
collision_mask = 21
motion_mode = 1
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
[node name="CollisionShape2D" type="CollisionShape2D" parent="." unique_id=572073196]
visible = false
shape = SubResource("CapsuleShape2D_hil2i")
[node name="Sprite2D" type="Sprite2D" parent="."]
[node name="Sprite2D" type="Sprite2D" parent="." unique_id=191340928]
material = SubResource("ShaderMaterial_q78ru")
texture = ExtResource("1_30hhw")
[node name="Jetpack Particles" type="GPUParticles2D" parent="."]
[node name="Jetpack Particles" type="GPUParticles2D" parent="." unique_id=1641064286]
z_index = -1
position = Vector2(-4, 16)
explosiveness = 0.5
fixed_fps = 24
process_material = SubResource("ParticleProcessMaterial_fd2du")
[node name="Jetpack Particles2" type="GPUParticles2D" parent="."]
[node name="Jetpack Particles2" type="GPUParticles2D" parent="." unique_id=962140317]
z_index = -1
position = Vector2(4, 16)
explosiveness = 0.5
fixed_fps = 24
process_material = SubResource("ParticleProcessMaterial_fd2du")
[node name="HealthComponent" type="Node2D" parent="." node_paths=PackedStringArray("HurtSfx")]
[node name="HealthComponent" type="Node2D" parent="." unique_id=1125679087 node_paths=PackedStringArray("HurtSfx")]
script = ExtResource("3_uyhuj")
Health = 0.25
MaxHealth = 0.25
HurtSfx = NodePath("../sfx_hurt")
[node name="sfx_hurt" type="AudioStreamPlayer2D" parent="."]
[node name="sfx_hurt" type="AudioStreamPlayer2D" parent="." unique_id=1006537001]
stream = ExtResource("3_fd2du")
bus = &"sfx"
[node name="sfx_shoot" type="AudioStreamPlayer2D" parent="."]
[node name="sfx_shoot" type="AudioStreamPlayer2D" parent="." unique_id=1437744637]
stream = ExtResource("4_rhq76")
bus = &"sfx"
[node name="DamageComponent" type="Node" parent="." node_paths=PackedStringArray("Area")]
[node name="DamageComponent" type="Node" parent="." unique_id=1923393563 node_paths=PackedStringArray("Area")]
script = ExtResource("6_q78ru")
Area = NodePath("../Hitbox")
[node name="PeriodicShootingComponent" type="Node" parent="." node_paths=PackedStringArray("BulletSpawnPointRight", "BulletSpawnPointLeft")]
[node name="PeriodicShootingComponent" type="Node" parent="." unique_id=1743534415 node_paths=PackedStringArray("BulletSpawnPointRight", "BulletSpawnPointLeft")]
script = ExtResource("7_weo6b")
BulletScene = ExtResource("7_4ajjm")
ShootInterval = 2.0
@@ -131,68 +133,68 @@ BulletSpawnPointRight = NodePath("../laser spawn point right")
BulletSpawnPointLeft = NodePath("../laser spawn point left")
ShootingIntervalVariation = 0.5
[node name="EnemyDeathComponent" type="Node" parent="." node_paths=PackedStringArray("CollisionShape", "Health")]
[node name="EnemyDeathComponent" type="Node" parent="." unique_id=1519565079 node_paths=PackedStringArray("CollisionShape", "Health")]
script = ExtResource("9_6p4k7")
TweenDuration = 0.1
CollisionShape = NodePath("../CollisionShape2D")
Health = NodePath("../HealthComponent")
[node name="FlashingComponent" type="Node" parent="." node_paths=PackedStringArray("Sprite", "HealthComponent")]
[node name="FlashingComponent" type="Node" parent="." unique_id=1761479670 node_paths=PackedStringArray("Sprite", "HealthComponent")]
process_mode = 3
script = ExtResource("10_jmybk")
Sprite = NodePath("../Sprite2D")
HealthComponent = NodePath("../HealthComponent")
[node name="HitComponent" type="Node" parent="." node_paths=PackedStringArray("Sprite", "Health", "HitFx")]
[node name="HitComponent" type="Node" parent="." unique_id=2106183592 node_paths=PackedStringArray("Sprite", "Health", "HitFx")]
script = ExtResource("11_2yvae")
Sprite = NodePath("../Sprite2D")
Health = NodePath("../HealthComponent")
HitFx = NodePath("../HitParticles")
[node name="StatusEffectComponent" type="Node" parent="."]
[node name="StatusEffectComponent" type="Node" parent="." unique_id=2016067092]
script = ExtResource("12_xlup2")
[node name="FireEffectComponent" type="Node" parent="." node_paths=PackedStringArray("Health", "StatusEffectComponent", "FireFX")]
[node name="FireEffectComponent" type="Node" parent="." unique_id=599378746 node_paths=PackedStringArray("Health", "StatusEffectComponent", "FireFX")]
script = ExtResource("13_mrjm6")
Health = NodePath("../HealthComponent")
StatusEffectComponent = NodePath("../StatusEffectComponent")
FireFX = NodePath("../FireFX")
[node name="IceEffectComponent" type="Node" parent="." node_paths=PackedStringArray("ComponentsToDisable", "StatusEffectComponent", "IceFx")]
[node name="IceEffectComponent" type="Node" parent="." unique_id=1515560540 node_paths=PackedStringArray("ComponentsToDisable", "StatusEffectComponent", "IceFx")]
script = ExtResource("14_pkino")
ComponentsToDisable = [NodePath("../PeriodicShootingComponent"), NodePath("../DamageComponent")]
StatusEffectComponent = NodePath("../StatusEffectComponent")
IceFx = NodePath("../Ice FX")
[node name="HitParticles" parent="." instance=ExtResource("13_xlup2")]
[node name="HitParticles" parent="." unique_id=579475644 instance=ExtResource("13_xlup2")]
position = Vector2(0, 1)
process_material = SubResource("ParticleProcessMaterial_qxfb0")
process_material = SubResource("ParticleProcessMaterial_q78ru")
[node name="FireFX" parent="." instance=ExtResource("14_mrjm6")]
[node name="FireFX" parent="." unique_id=1136026281 instance=ExtResource("14_mrjm6")]
position = Vector2(0, 9)
emitting = false
amount = 2048
[node name="Ice FX" parent="." instance=ExtResource("15_pkino")]
[node name="Ice FX" parent="." unique_id=275134518 instance=ExtResource("15_pkino")]
visible = false
position = Vector2(1, 0)
scale = Vector2(0.684407, 0.929677)
[node name="laser spawn point right" type="Node2D" parent="."]
[node name="laser spawn point right" type="Node2D" parent="." unique_id=915998238]
position = Vector2(8, -2)
[node name="laser spawn point left" type="Node2D" parent="."]
[node name="laser spawn point left" type="Node2D" parent="." unique_id=1180867485]
position = Vector2(-9, -2)
[node name="Hitbox" type="Area2D" parent="."]
[node name="Hitbox" type="Area2D" parent="." unique_id=1699649839]
collision_layer = 8
collision_mask = 20
[node name="CollisionShape2D" type="CollisionShape2D" parent="Hitbox"]
[node name="CollisionShape2D" type="CollisionShape2D" parent="Hitbox" unique_id=379520850]
position = Vector2(0, 2)
shape = SubResource("RectangleShape2D_cmp1h")
[node name="PathFollowerComponent" type="Node2D" parent="."]
[node name="PathFollowerComponent" type="Node2D" parent="." unique_id=1706994676]
script = ExtResource("18_q78ru")
ShouldRotate = false
metadata/_custom_type_script = "uid://b4hvq2i66fjhi"

View File

@@ -0,0 +1,32 @@
[gd_scene load_steps=5 format=3 uid="uid://0idmnkwids1r"]
[ext_resource type="Texture2D" uid="uid://djifxc5x0dyrw" path="res://sprites/ppc_tileset.png" id="1_sprite"]
[ext_resource type="Script" uid="uid://r4jybneigfcn" path="res://scripts/components/CollectableComponent.cs" id="2_collectable"]
[ext_resource type="Script" uid="uid://bjln6jb1sigx2" path="res://scripts/components/FadeAwayComponent.cs" id="3_fadeaway"]
[sub_resource type="CircleShape2D" id="CircleShape2D_pickup"]
radius = 12.0
[node name="SkillPickup" type="Area2D" groups=["Collectables"]]
collision_layer = 2
collision_mask = 4
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
shape = SubResource("CircleShape2D_pickup")
[node name="Sprite2D" type="Sprite2D" parent="."]
texture = ExtResource("1_sprite")
hframes = 12
vframes = 12
frame = 24
[node name="CollectableComponent" type="Node" parent="." node_paths=PackedStringArray("Area2D", "CollisionShape")]
script = ExtResource("2_collectable")
Area2D = NodePath("..")
CollisionShape = NodePath("../CollisionShape2D")
[node name="FadeAwayComponent" type="Node" parent="." node_paths=PackedStringArray("Sprite", "Area")]
script = ExtResource("3_fadeaway")
Sprite = NodePath("../Sprite2D")
FadeDuration = 0.5
Area = NodePath("..")

View File

@@ -1,15 +1,17 @@
[gd_scene load_steps=7 format=3 uid="uid://b4eifkc31jsun"]
[gd_scene format=3 uid="uid://b4eifkc31jsun"]
[ext_resource type="Script" uid="uid://c6eoi3ymefc0x" path="res://Autoloads/GameManager.cs" id="1_t2tr6"]
[ext_resource type="PackedScene" uid="uid://dwr3avefuwnpy" path="res://scenes/level_tutorial_1.tscn" id="2_7rb6w"]
[ext_resource type="PackedScene" uid="uid://bol7g83v2accs" path="res://scenes/level_village_1.tscn" id="2_bentb"]
[ext_resource type="PackedScene" uid="uid://chqb11pfoqmeb" path="res://scenes/level_village_2.tscn" id="3_ajlkg"]
[ext_resource type="PackedScene" uid="uid://h60obxmju6mo" path="res://scenes/level_village_3.tscn" id="4_se5tb"]
[ext_resource type="PackedScene" uid="uid://bhad760x3vvco" path="res://scenes/level_village_4.tscn" id="5_mnosh"]
[ext_resource type="PackedScene" uid="uid://bbwef3n2gjkt8" path="res://scenes/level_village_5.tscn" id="6_7rb6w"]
[ext_resource type="Script" uid="uid://chrhjch4ymfvr" path="res://scripts/Screenshot.cs" id="6_bbtu1"]
[node name="GameManager" type="Node"]
[node name="GameManager" type="Node" unique_id=2076336659]
script = ExtResource("1_t2tr6")
LevelScenes = Array[PackedScene]([ExtResource("2_bentb"), ExtResource("3_ajlkg"), ExtResource("4_se5tb"), ExtResource("5_mnosh")])
LevelScenes = Array[PackedScene]([ExtResource("2_7rb6w"), ExtResource("2_bentb"), ExtResource("3_ajlkg"), ExtResource("4_se5tb"), ExtResource("5_mnosh"), ExtResource("6_7rb6w")])
[node name="Screenshot" type="Node" parent="."]
[node name="Screenshot" type="Node" parent="." unique_id=812485936]
script = ExtResource("6_bbtu1")

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=2 format=3 uid="uid://dtxkjif7prm70"]
[gd_scene load_steps=2 format=3 uid="uid://bimyb8suadq3u"]
[ext_resource type="Script" uid="uid://dl7vthhurirwc" path="res://scripts/components/XRayVisionSkillComponent.cs" id="1_ebn6n"]

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=13 format=3 uid="uid://bi6v7u17vg1ww"]
[gd_scene load_steps=12 format=3 uid="uid://bi6v7u17vg1ww"]
[ext_resource type="Script" uid="uid://dru77vj07e18s" path="res://Autoloads/SkillManager.cs" id="1_31033"]
[ext_resource type="Script" uid="uid://d4crrfmbgxnqf" path="res://scripts/Resources/SkillData.cs" id="2_87da4"]
@@ -6,13 +6,12 @@
[ext_resource type="Resource" uid="uid://cdp8sex36vdq2" path="res://resources/skills/explosive_brick.tres" id="4_53vnv"]
[ext_resource type="Resource" uid="uid://cr5lo4h8wm0jc" path="res://resources/skills/fire_brick.tres" id="5_77gav"]
[ext_resource type="Resource" uid="uid://ceakv6oqob6m7" path="res://resources/skills/ice_brick.tres" id="6_gib8v"]
[ext_resource type="Resource" uid="uid://d3bjre2etov1n" path="res://resources/skills/magnetic.tres" id="7_6wy8o"]
[ext_resource type="Resource" uid="uid://bxsgq8703qx4u" path="res://resources/skills/double_jump.tres" id="8_87da4"]
[ext_resource type="Resource" uid="uid://cseilsspimw1n" path="res://resources/skills/ground_pound_skill.tres" id="9_77gav"]
[ext_resource type="Resource" uid="uid://c5dj06l86winx" path="res://resources/skills/xray_vision.tres" id="10_gib8v"]
[ext_resource type="Resource" uid="uid://d3bjre2etov1n" path="res://resources/skills/magnetic.tres" id="10_gib8v"]
[ext_resource type="Resource" uid="uid://d12defdtmlk0u" path="res://resources/skills/brick_shield.tres" id="11_6wy8o"]
[ext_resource type="Resource" uid="uid://dghnl301o1aiy" path="res://resources/skills/brick_armor.tres" id="12_gib8v"]
[node name="SkillManager" type="Node"]
script = ExtResource("1_31033")
AvailableSkills = Array[ExtResource("2_87da4")]([ExtResource("3_shjvi"), ExtResource("4_53vnv"), ExtResource("5_77gav"), ExtResource("6_gib8v"), ExtResource("7_6wy8o"), ExtResource("8_87da4"), ExtResource("9_77gav"), ExtResource("10_gib8v"), ExtResource("11_6wy8o"), ExtResource("12_gib8v")])
AvailableSkills = Array[ExtResource("2_87da4")]([ExtResource("3_shjvi"), ExtResource("4_53vnv"), ExtResource("5_77gav"), ExtResource("6_gib8v"), ExtResource("10_gib8v"), ExtResource("8_87da4"), ExtResource("9_77gav"), ExtResource("11_6wy8o"), ExtResource("12_gib8v")])

View File

@@ -53,11 +53,13 @@ size_flags_vertical = 3
[node name="Input Settings Button" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
disabled = true
text = "INPUT_BUTTON"
flat = true
[node name="Display Settings Button" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
disabled = true
text = "DISPLAY_BUTTON"
flat = true
@@ -68,6 +70,7 @@ flat = true
[node name="Gameplay Settings Button" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
disabled = true
text = "GAMEPLAY_BUTTON"
flat = true

View File

@@ -8,6 +8,10 @@
config_version=5
[animation]
compatibility/default_parent_skeleton_in_mesh_instance_3d=true
[application]
config/name="Mr. Brick Adventures"
@@ -19,33 +23,39 @@ config/version="in-dev"
run/main_scene="uid://cl00e2ocomk3m"
config/use_custom_user_dir=true
config/custom_user_dir_name="MrBrickAdventures"
config/features=PackedStringArray("4.4", "C#", "GL Compatibility")
config/features=PackedStringArray("4.6", "C#", "GL Compatibility")
run/max_fps=180
boot_splash/bg_color=Color(0, 0, 0, 1)
boot_splash/show_image=false
boot_splash/fullsize=false
boot_splash/stretch_mode=0
boot_splash/use_filter=false
config/icon="uid://jix7wdn0isr3"
[autoload]
GameManager="*res://objects/game_manager.tscn"
PhantomCameraManager="*res://addons/phantom_camera/scripts/managers/phantom_camera_manager.gd"
AudioController="*res://objects/audio_controller.tscn"
UIManager="*res://Autoloads/UIManager.cs"
ConfigFileHandler="*res://Autoloads/ConfigFileHandler.cs"
SaveSystem="*res://Autoloads/SaveSystem.cs"
ConsoleManager="*res://Autoloads/ConsoleManager.cs"
LimboConsole="*res://addons/limbo_console/limbo_console.gd"
LimboConsole="*uid://dyxornv8vwibg"
DialogueManager="*res://addons/dialogue_manager/dialogue_manager.gd"
EventBus="*res://Autoloads/EventBus.cs"
SteamManager="*res://Autoloads/SteamManager.cs"
AchievementManager="*res://objects/achievement_manager.tscn"
SkillManager="*res://objects/skill_manager.tscn"
FloatingTextManager="*res://objects/floating_text_manager.tscn"
EventBus="*res://Autoloads/EventBus.cs"
StatisticsManager="*res://Autoloads/StatisticsManager.cs"
SpeedRunManager="res://Autoloads/SpeedRunManager.cs"
GhostManager="res://objects/ghost_manager.tscn"
StatisticsEventHandler="*res://scripts/Events/StatisticsEventHandler.cs"
CoinStateHandler="*res://scripts/Events/CoinStateHandler.cs"
LevelStateHandler="*res://scripts/Events/LevelStateHandler.cs"
LivesStateHandler="*res://scripts/Events/LivesStateHandler.cs"
SkillCollectHandler="*res://scripts/Events/SkillCollectHandler.cs"
GameStateStore="*res://Autoloads/GameStateStore.cs"
GameManager="*res://objects/game_manager.tscn"
[debug]
@@ -80,6 +90,17 @@ movie_writer/fps=24
enabled=PackedStringArray("res://addons/dialogue_manager/plugin.cfg", "res://addons/limbo_console/plugin.cfg", "res://addons/phantom_camera/plugin.cfg")
[file_customization]
folder_colors={
"res://objects/": "red",
"res://objects/entities/": "yellow",
"res://resources/": "orange",
"res://scenes/": "purple",
"res://scripts/": "teal",
"res://sprites/": "green"
}
[global_group]
coins=""
@@ -89,9 +110,9 @@ Collectables=""
[gui]
theme/default_font_antialiasing=0
theme/default_theme_scale=0.5
theme/custom_font="res://fonts/PressStart2P-Regular.ttf"
theme/default_font_antialiasing=0
[input]
@@ -182,18 +203,15 @@ screenshot={
}
limbo_console_toggle={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":96,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
"events": []
}
limbo_auto_complete_reverse={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194306,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
"events": []
}
limbo_console_search_history={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":true,"meta_pressed":false,"pressed":false,"keycode":82,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
"events": []
}
[internationalization]

View File

@@ -1,8 +1,8 @@
[gd_resource type="Resource" load_steps=2 format=3 uid="uid://bsnr5v2b2mfsl"]
[gd_resource type="Resource" format=3 uid="uid://bsnr5v2b2mfsl"]
[ext_resource type="Script" uid="uid://gptsgaw3agkf" path="res://scripts/Resources/CollectableResource.cs" id="1_wfbgp"]
[resource]
script = ExtResource("1_wfbgp")
Amount = 5.0
Type = 0
Type = 1

View File

@@ -1,8 +1,8 @@
[gd_resource type="Resource" load_steps=2 format=3 uid="uid://b6xqotmke54x"]
[gd_resource type="Resource" format=3 uid="uid://b6xqotmke54x"]
[ext_resource type="Script" uid="uid://gptsgaw3agkf" path="res://scripts/Resources/CollectableResource.cs" id="1_5mosu"]
[resource]
script = ExtResource("1_5mosu")
Amount = 50.0
Type = 0
Type = 1

View File

@@ -1,8 +1,8 @@
[gd_resource type="Resource" load_steps=2 format=3 uid="uid://b6apusc0jmi3x"]
[gd_resource type="Resource" format=3 uid="uid://b6apusc0jmi3x"]
[ext_resource type="Script" uid="uid://gptsgaw3agkf" path="res://scripts/Resources/CollectableResource.cs" id="1_d8txc"]
[resource]
script = ExtResource("1_d8txc")
Amount = 1.0
Type = 1
Type = 2

View File

@@ -1,8 +1,8 @@
[gd_resource type="Resource" load_steps=2 format=3 uid="uid://vql535ckoeqm"]
[gd_resource type="Resource" format=3 uid="uid://vql535ckoeqm"]
[ext_resource type="Script" uid="uid://gptsgaw3agkf" path="res://scripts/Resources/CollectableResource.cs" id="1_7pquc"]
[resource]
script = ExtResource("1_7pquc")
Amount = 1.0
Type = 0
Type = 1

View File

@@ -0,0 +1,9 @@
[gd_resource type="Resource" format=3 uid="uid://dfyp1n4p52ydm"]
[ext_resource type="Script" uid="uid://gptsgaw3agkf" path="res://scripts/Resources/CollectableResource.cs" id="1_script"]
[ext_resource type="Resource" uid="uid://bxsgq8703qx4u" path="res://resources/skills/double_jump.tres" id="2_skill"]
[resource]
script = ExtResource("1_script")
Type = 4
Skill = ExtResource("2_skill")

View File

@@ -1,9 +1,8 @@
[gd_resource type="Resource" load_steps=2 format=3 uid="uid://2tl3yoh202no"]
[gd_resource type="Resource" format=3 uid="uid://2tl3yoh202no"]
[ext_resource type="Script" uid="uid://gptsgaw3agkf" path="res://scripts/Resources/CollectableResource.cs" id="1_brkhb"]
[resource]
script = ExtResource("1_brkhb")
Amount = 0.25
Type = 2
metadata/_custom_type_script = "uid://cb5f0mx0hrt3b"
Type = 3

View File

@@ -1,8 +1,8 @@
[gd_resource type="Resource" load_steps=2 format=3 uid="uid://bws2xldndlre1"]
[gd_resource type="Resource" format=3 uid="uid://bws2xldndlre1"]
[ext_resource type="Script" uid="uid://gptsgaw3agkf" path="res://scripts/Resources/CollectableResource.cs" id="1_clntw"]
[resource]
script = ExtResource("1_clntw")
Amount = 50.0
Type = 0
Type = 1

View File

@@ -0,0 +1,8 @@
[gd_resource type="Resource" script_class="LevelResource" load_steps=2 format=3 uid="uid://qld841xv5ui0"]
[ext_resource type="Script" uid="uid://c2h0pqhxiqswe" path="res://scripts/Resources/LevelResource.cs" id="1_chped"]
[resource]
script = ExtResource("1_chped")
LevelName = "LEVEL_5_NAME"
ScenePath = "res://scenes/level_village_5.tscn"

View File

@@ -1,4 +1,4 @@
[gd_resource type="Resource" script_class="SkillData" load_steps=5 format=3 uid="uid://dghnl301o1aiy"]
[gd_resource type="Resource" script_class="SkillData" format=3 uid="uid://dghnl301o1aiy"]
[ext_resource type="PackedScene" uid="uid://cdeh7wfc62fr4" path="res://objects/player_skills/brick_armor_skill_component.tscn" id="1_aqcna"]
[ext_resource type="Script" uid="uid://d4crrfmbgxnqf" path="res://scripts/Resources/SkillData.cs" id="1_unqwr"]
@@ -17,9 +17,6 @@ metadata/_custom_type_script = "uid://dwb0e05pewcsn"
script = ExtResource("1_unqwr")
Name = "BRICK_ARMOR"
Description = "BRICK_ARMOR_DESCRIPTION"
IsActive = false
Level = 1
Type = 1
Node = ExtResource("1_aqcna")
Upgrades = Array[ExtResource("2_kqsqd")]([SubResource("Resource_xwv1u"), SubResource("Resource_xwv1u")])
metadata/_custom_type_script = "uid://d4crrfmbgxnqf"

View File

@@ -1,4 +1,4 @@
[gd_resource type="Resource" script_class="SkillData" load_steps=6 format=3 uid="uid://d12defdtmlk0u"]
[gd_resource type="Resource" script_class="SkillData" format=3 uid="uid://d12defdtmlk0u"]
[ext_resource type="Script" uid="uid://d4crrfmbgxnqf" path="res://scripts/Resources/SkillData.cs" id="1_m360g"]
[ext_resource type="PackedScene" uid="uid://blwk5qduvdnxv" path="res://objects/player_skills/brick_shield_skill_component.tscn" id="1_xjknp"]
@@ -26,8 +26,6 @@ metadata/_custom_type_script = "uid://dwb0e05pewcsn"
script = ExtResource("1_m360g")
Name = "BRICK_SHIELD"
Description = "BRICK_SHIELD_DESCRIPTION"
IsActive = false
Level = 1
Type = 2
Node = ExtResource("1_xjknp")
Upgrades = Array[ExtResource("2_lr0w4")]([SubResource("Resource_mu2sy"), SubResource("Resource_5ab4a")])

View File

@@ -1,4 +1,4 @@
[gd_resource type="Resource" script_class="SkillData" load_steps=5 format=3 uid="uid://bxsgq8703qx4u"]
[gd_resource type="Resource" script_class="SkillData" format=3 uid="uid://bxsgq8703qx4u"]
[ext_resource type="Script" uid="uid://d4crrfmbgxnqf" path="res://scripts/Resources/SkillData.cs" id="1_p5qvt"]
[ext_resource type="PackedScene" uid="uid://dwaxbojb44a6l" path="res://objects/player_skills/double_jump_skill.tscn" id="1_t7o84"]
@@ -8,15 +8,12 @@
script = ExtResource("2_kywbf")
Cost = 80
Description = ""
Properties = Dictionary[String, Variant]({})
metadata/_custom_type_script = "uid://dwb0e05pewcsn"
[resource]
script = ExtResource("1_p5qvt")
Name = "DOUBLE_JUMP"
Description = "DOUBLE_JUMP_DESCRIPTION"
IsActive = false
Level = 1
Type = 2
Node = ExtResource("1_t7o84")
Upgrades = Array[ExtResource("2_kywbf")]([SubResource("Resource_0fn2n")])

View File

@@ -1,4 +1,4 @@
[gd_resource type="Resource" script_class="SkillData" load_steps=5 format=3 uid="uid://cseilsspimw1n"]
[gd_resource type="Resource" script_class="SkillData" format=3 uid="uid://cseilsspimw1n"]
[ext_resource type="PackedScene" uid="uid://lu3wvpqefekn" path="res://objects/player_skills/ground_pound_skill_component.tscn" id="1_auljr"]
[ext_resource type="Script" uid="uid://d4crrfmbgxnqf" path="res://scripts/Resources/SkillData.cs" id="1_i1qac"]
@@ -8,15 +8,12 @@
script = ExtResource("2_tkhf7")
Cost = 300
Description = ""
Properties = Dictionary[String, Variant]({})
metadata/_custom_type_script = "uid://dwb0e05pewcsn"
[resource]
script = ExtResource("1_i1qac")
Name = "GROUND_POUND_SKILL"
Description = "GROUND_POUND_SKILL_DESCRIPTION"
IsActive = false
Level = 1
Type = 2
Node = ExtResource("1_auljr")
Upgrades = Array[ExtResource("2_tkhf7")]([SubResource("Resource_upxa7")])

View File

@@ -0,0 +1,8 @@
[gd_resource type="Resource" script_class="SkillData" format=3 uid="uid://mwqffpqncwxo"]
[ext_resource type="Script" uid="uid://dwb0e05pewcsn" path="res://scripts/Resources/SkillUpgrade.cs" id="1_vptvl"]
[ext_resource type="Script" uid="uid://d4crrfmbgxnqf" path="res://scripts/Resources/SkillData.cs" id="2_rw7jf"]
[resource]
script = ExtResource("2_rw7jf")
metadata/_custom_type_script = "uid://d4crrfmbgxnqf"

View File

@@ -1,34 +0,0 @@
[gd_resource type="Resource" script_class="SkillData" load_steps=6 format=3 uid="uid://c5dj06l86winx"]
[ext_resource type="PackedScene" uid="uid://dtxkjif7prm70" path="res://objects/player_skills/x_ray_vision_skill_component.tscn" id="1_ax2d8"]
[ext_resource type="Script" uid="uid://d4crrfmbgxnqf" path="res://scripts/Resources/SkillData.cs" id="1_g8qe3"]
[ext_resource type="Script" uid="uid://dwb0e05pewcsn" path="res://scripts/Resources/SkillUpgrade.cs" id="2_o726x"]
[sub_resource type="Resource" id="Resource_72ltj"]
script = ExtResource("2_o726x")
Cost = 200
Description = ""
Properties = Dictionary[String, Variant]({
"duration": 5.0
})
metadata/_custom_type_script = "uid://dwb0e05pewcsn"
[sub_resource type="Resource" id="Resource_2kdfi"]
script = ExtResource("2_o726x")
Cost = 275
Description = ""
Properties = Dictionary[String, Variant]({
"duration": 10.0
})
metadata/_custom_type_script = "uid://dwb0e05pewcsn"
[resource]
script = ExtResource("1_g8qe3")
Name = "XRAY_VISION"
Description = "XRAY_VISION_DESCRIPTION"
IsActive = false
Level = 1
Type = 1
Node = ExtResource("1_ax2d8")
Upgrades = Array[ExtResource("2_o726x")]([SubResource("Resource_72ltj"), SubResource("Resource_2kdfi")])
metadata/_custom_type_script = "uid://d4crrfmbgxnqf"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
[gd_resource type="TileSet" load_steps=3 format=3 uid="uid://ccffmjebvuoaj"]
[gd_resource type="TileSet" format=3 uid="uid://ccffmjebvuoaj"]
[ext_resource type="Texture2D" uid="uid://dxvevrm15uus1" path="res://sprites/flowers_tileset.png" id="1_6pkiv"]

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,9 @@
[gd_scene load_steps=26 format=4 uid="uid://chqb11pfoqmeb"]
[gd_scene load_steps=27 format=4 uid="uid://chqb11pfoqmeb"]
[ext_resource type="PackedScene" uid="uid://bqi5s710xb1ju" path="res://objects/entities/brick_player.tscn" id="1_wcma7"]
[ext_resource type="PackedScene" uid="uid://cawlpch2lk3a2" path="res://objects/level/world_environment.tscn" id="2_ot3dy"]
[ext_resource type="PackedScene" uid="uid://6foggu31cu14" path="res://objects/level/ui_layer.tscn" id="3_cjqhe"]
[ext_resource type="Resource" uid="uid://dlwkbjw1l33uq" path="res://resources/levels/village/village_2.tres" id="4_h4uvs"]
[ext_resource type="PackedScene" uid="uid://cywsu7yrtjdog" path="res://objects/level/global_light.tscn" id="4_wykfl"]
[ext_resource type="PackedScene" uid="uid://cb0mnye1ki5a6" path="res://objects/level/camera_2d.tscn" id="5_8nvkd"]
[ext_resource type="Script" uid="uid://d23haq52m7ulv" path="res://addons/phantom_camera/scripts/phantom_camera/phantom_camera_2d.gd" id="6_ono4h"]
@@ -54,7 +55,6 @@ color_ramp = SubResource("GradientTexture1D_f1fvy")
script = ExtResource("7_y1tp2")
duration = 0.1
transition = 3
ease = 2
[node name="World" type="Node2D"]
@@ -75,6 +75,9 @@ process_mode = 4
[node name="HUD" parent="UI Layer" index="0" node_paths=PackedStringArray("Health")]
Health = NodePath("../../Brick Player/HealthComponent")
[node name="DeathScreen" parent="UI Layer" index="1"]
CurrentLevel = ExtResource("4_h4uvs")
[node name="Marketplace" parent="UI Layer" index="3" node_paths=PackedStringArray("SkillUnlockerComponent", "ComponentsToDisable")]
SkillUnlockerComponent = NodePath("../../Brick Player/SkillUnlockerComponent")
ComponentsToDisable = [NodePath("../../Brick Player")]
@@ -132,6 +135,7 @@ tile_set = ExtResource("13_lc0ll")
[node name="ExitLevel" parent="." instance=ExtResource("15_hcsb6")]
position = Vector2(987, -776)
AchievementId = "level_complete_2"
[node name="Child" parent="." instance=ExtResource("16_lc0ll")]
position = Vector2(-1326, 176)
@@ -156,18 +160,18 @@ position = Vector2(1272, -143)
[node name="Enemy" parent="Enemies" instance=ExtResource("18_girek")]
position = Vector2(-1526, 256)
[node name="Enemy2" parent="Enemies" instance=ExtResource("18_girek")]
position = Vector2(-1098, 254)
[node name="Enemy3" parent="Enemies" instance=ExtResource("18_girek")]
position = Vector2(-1190, 175)
[node name="Enemy4" parent="Enemies" instance=ExtResource("18_girek")]
position = Vector2(-1326, 320)
[node name="Enemy6" parent="Enemies" instance=ExtResource("18_girek")]
position = Vector2(1648, -352)
[node name="Enemy9" parent="Enemies" instance=ExtResource("16_h4uvs")]
position = Vector2(-1097, 255)
[node name="Enemy2" parent="Enemies" instance=ExtResource("16_h4uvs")]
position = Vector2(-1326, 320)
[node name="Lever" parent="." instance=ExtResource("20_h4uvs")]
position = Vector2(-231, -776)

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=31 format=4 uid="uid://h60obxmju6mo"]
[gd_scene load_steps=34 format=4 uid="uid://h60obxmju6mo"]
[ext_resource type="PackedScene" uid="uid://dyp4i4ru2j2jh" path="res://objects/fxs/explosion_fx.tscn" id="1_p30ax"]
[ext_resource type="PackedScene" uid="uid://dx80ivlvuuew4" path="res://objects/fxs/fire_fx.tscn" id="2_a7yjf"]
@@ -16,11 +16,14 @@
[ext_resource type="Script" uid="uid://ccfft4b8rwgbo" path="res://addons/phantom_camera/scripts/resources/tween_resource.gd" id="14_k7w2w"]
[ext_resource type="PackedScene" uid="uid://d0s2abysa86rq" path="res://objects/entities/child.tscn" id="15_dv6gh"]
[ext_resource type="PackedScene" uid="uid://6foggu31cu14" path="res://objects/level/ui_layer.tscn" id="16_nr2eo"]
[ext_resource type="Resource" uid="uid://b63u5qfp8p7pv" path="res://resources/levels/village/village_3.tres" id="17_m6h4x"]
[ext_resource type="PackedScene" uid="uid://b4pdt1gv2ymyi" path="res://objects/tooltip.tscn" id="18_l3a7y"]
[ext_resource type="PackedScene" uid="uid://cawlpch2lk3a2" path="res://objects/level/world_environment.tscn" id="20_embdf"]
[ext_resource type="PackedScene" uid="uid://cywsu7yrtjdog" path="res://objects/level/global_light.tscn" id="21_fytod"]
[ext_resource type="PackedScene" uid="uid://bqom4cm7r18db" path="res://objects/entities/killzone.tscn" id="21_p30ax"]
[ext_resource type="PackedScene" uid="uid://12jnkdygpxwc" path="res://objects/entities/exit_level.tscn" id="22_a7yjf"]
[ext_resource type="PackedScene" uid="uid://t6h2ra7kjyq" path="res://objects/entities/small_heal_potion.tscn" id="23_m6h4x"]
[ext_resource type="PackedScene" uid="uid://dk2cu8qs7odib" path="res://objects/entities/double_jump_skill_pickup.tscn" id="24_6fdf4"]
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_j7bvy"]
texture = ExtResource("7_uvxky")
@@ -234,7 +237,6 @@ color_ramp = SubResource("GradientTexture1D_f1fvy")
script = ExtResource("14_k7w2w")
duration = 0.25
transition = 3
ease = 2
[node name="World" type="Node2D"]
@@ -254,7 +256,7 @@ tile_map_data = PackedByteArray("AABiAAcAAQAEAAMAAABiAAYAAQAEAAIAAABiAAUAAQAEAAI
tile_set = ExtResource("4_6fdf4")
[node name="Entities layer" type="TileMapLayer" parent="."]
tile_map_data = PackedByteArray("AABAAAAAAAAJAAYAAABFAAAAAAALAAYAAABEAAAAAAAKAAYAAABDAAAAAAAKAAYAAABCAAAAAAAKAAYAAABBAAAAAAAKAAYAAABAAPr/AAAJAAYAAABBAPr/AAAKAAYAAABCAPr/AAAKAAYAAABDAPr/AAAKAAYAAABEAPr/AAAKAAYAAABFAPr/AAAKAAYAAABGAPr/AAAKAAYAAABHAPr/AAAKAAYAAABIAPr/AAAKAAYAAABJAPr/AAAKAAYAAABKAPr/AAAKAAYAAABLAPr/AAALAAYAAABjAP7/AAAHAAQAAAB8AP7/AAAKAAQAAABkAP7/AQAAAAAAAgBlAP7/AQAAAAAAAgBmAP7/AQAAAAAAAgBnAP7/AQAAAAAAAgBoAP7/AQAAAAAAAgBpAP7/AQAAAAAAAgBsAP7/AQAAAAAAAgBtAP7/AQAAAAAAAgBrAP7/AQAAAAAAAgBqAP7/AQAAAAAAAgBuAP7/AQAAAAAAAgBvAP7/AQAAAAAAAgBwAP7/AQAAAAAAAgBxAP7/AQAAAAAAAgByAP7/AQAAAAAAAgBzAP7/AQAAAAAAAgB0AP7/AQAAAAAAAgB1AP7/AQAAAAAAAgB2AP7/AQAAAAAAAgB3AP7/AQAAAAAAAgB4AP7/AQAAAAAAAgB5AP7/AQAAAAAAAgB6AP7/AQAAAAAAAgB7AP7/AQAAAAAAAgCJAPr/AAAJAAYAAACKAPr/AAAKAAYAAACLAPr/AAALAAYAAABOAOz/AQAAAAAAAQBNAOz/AQAAAAAAAQBMAOz/AQAAAAAAAQBNAOv/AQAAAAAAAQBDAOf/AQAAAAAAAQBCAOf/AQAAAAAAAQBBAOf/AQAAAAAAAQBCAOb/AQAAAAAAAQBIAOP/AQAAAAAAAQBHAOP/AQAAAAAAAQBGAOP/AQAAAAAAAQBHAOL/AQAAAAAAAQBBAN7/AQAAAAAAAQBCAN7/AQAAAAAAAQBDAN7/AQAAAAAAAQBCAN3/AQAAAAAAAQBDAOb/AQAAAAAAAQBGAOL/AQAAAAAAAQBDAN3/AQAAAAAAAQBMAOv/AQAAAAAAAQCRAOv/AQAAAAAAAQCRAOr/AQAAAAAAAQCRAOn/AQAAAAAAAQCSAOn/AQAAAAAAAQCTAOn/AQAAAAAAAQCTAOr/AQAAAAAAAQCTAOv/AQAAAAAAAQCSAOv/AQAAAAAAAQCSAOr/AQAAAAAAAQCAAOv/AQAAAAAAAQB/AOv/AQAAAAAAAQB+AOv/AQAAAAAAAQB+AOr/AQAAAAAAAQB/AOr/AQAAAAAAAQBAAP//AQAAAAAAAQBBAP//AQAAAAAAAQBCAP//AQAAAAAAAQBDAP//AQAAAAAAAQBEAP//AQAAAAAAAQClAP//AQAAAAAAAQCmAP//AQAAAAAAAQCnAP//AQAAAAAAAQCnAP7/AQAAAAAAAQClAP7/AQAAAAAAAQD4APT/AQAAAAAAAwC5AP3/AQAAAAAAAwCnACwAAQAAAAAABQChACwAAQAAAAAABgCvACwAAQAAAAAABgA2ANz/AQAAAAAABwD5APX/AQAAAAAACAABAQIAAQAAAAAACQA=")
tile_map_data = PackedByteArray("AABAAAAAAAAJAAYAAABFAAAAAAALAAYAAABEAAAAAAAKAAYAAABDAAAAAAAKAAYAAABCAAAAAAAKAAYAAABBAAAAAAAKAAYAAABAAPr/AAAJAAYAAABBAPr/AAAKAAYAAABCAPr/AAAKAAYAAABDAPr/AAAKAAYAAABEAPr/AAAKAAYAAABFAPr/AAAKAAYAAABGAPr/AAAKAAYAAABHAPr/AAAKAAYAAABIAPr/AAAKAAYAAABJAPr/AAAKAAYAAABKAPr/AAAKAAYAAABLAPr/AAALAAYAAABjAP7/AAAHAAQAAAB8AP7/AAAKAAQAAABkAP7/AQAAAAAAAgBlAP7/AQAAAAAAAgBmAP7/AQAAAAAAAgBnAP7/AQAAAAAAAgBoAP7/AQAAAAAAAgBpAP7/AQAAAAAAAgBsAP7/AQAAAAAAAgBtAP7/AQAAAAAAAgBrAP7/AQAAAAAAAgBqAP7/AQAAAAAAAgBuAP7/AQAAAAAAAgBvAP7/AQAAAAAAAgBwAP7/AQAAAAAAAgBxAP7/AQAAAAAAAgByAP7/AQAAAAAAAgBzAP7/AQAAAAAAAgB0AP7/AQAAAAAAAgB1AP7/AQAAAAAAAgB2AP7/AQAAAAAAAgB3AP7/AQAAAAAAAgB4AP7/AQAAAAAAAgB5AP7/AQAAAAAAAgB6AP7/AQAAAAAAAgB7AP7/AQAAAAAAAgCJAPr/AAAJAAYAAACKAPr/AAAKAAYAAACLAPr/AAALAAYAAABOAOz/AQAAAAAAAQBNAOz/AQAAAAAAAQBMAOz/AQAAAAAAAQBNAOv/AQAAAAAAAQBDAOf/AQAAAAAAAQBCAOf/AQAAAAAAAQBBAOf/AQAAAAAAAQBCAOb/AQAAAAAAAQBIAOP/AQAAAAAAAQBHAOP/AQAAAAAAAQBGAOP/AQAAAAAAAQBHAOL/AQAAAAAAAQBBAN7/AQAAAAAAAQBCAN7/AQAAAAAAAQBDAN7/AQAAAAAAAQBCAN3/AQAAAAAAAQBDAOb/AQAAAAAAAQBGAOL/AQAAAAAAAQBDAN3/AQAAAAAAAQBMAOv/AQAAAAAAAQCRAOv/AQAAAAAAAQCRAOr/AQAAAAAAAQCRAOn/AQAAAAAAAQCSAOn/AQAAAAAAAQCTAOn/AQAAAAAAAQCTAOr/AQAAAAAAAQCTAOv/AQAAAAAAAQCSAOv/AQAAAAAAAQCSAOr/AQAAAAAAAQCAAOv/AQAAAAAAAQB/AOv/AQAAAAAAAQB+AOv/AQAAAAAAAQB+AOr/AQAAAAAAAQB/AOr/AQAAAAAAAQBAAP//AQAAAAAAAQBBAP//AQAAAAAAAQBCAP//AQAAAAAAAQBDAP//AQAAAAAAAQBEAP//AQAAAAAAAQClAP//AQAAAAAAAQCmAP//AQAAAAAAAQCnAP//AQAAAAAAAQCnAP7/AQAAAAAAAQClAP7/AQAAAAAAAQD4APT/AQAAAAAAAwC5AP3/AQAAAAAAAwCnACwAAQAAAAAABQChACwAAQAAAAAABgCvACwAAQAAAAAABgA2ANz/AQAAAAAABwD5APX/AQAAAAAACAA=")
tile_set = ExtResource("5_ipoec")
[node name="Foreground layer" type="TileMapLayer" parent="."]
@@ -272,6 +274,10 @@ tile_map_data = PackedByteArray("AAA2AVkAAAAAAAAAAAA6AVkAAAACAAAAAAA8AVkAAAABAAA
tile_set = ExtResource("9_ma4yh")
collision_enabled = false
[node name="ExitLevel" parent="." instance=ExtResource("22_a7yjf")]
position = Vector2(4138, 40)
AchievementId = "level_complete_3"
[node name="Enemies" type="Node2D" parent="."]
process_mode = 1
@@ -295,7 +301,7 @@ z_index = 5
position = Vector2(903, -118)
metadata/_edit_group_ = true
[node name="HitParticles" parent="Brick Player" index="25"]
[node name="HitParticles" parent="Brick Player" index="23"]
process_material = SubResource("ParticleProcessMaterial_lgb3u")
[node name="Camera2D" parent="." instance=ExtResource("12_qhkyq")]
@@ -328,15 +334,20 @@ position = Vector2(880, -578)
[node name="HUD" parent="UI Layer" index="0" node_paths=PackedStringArray("Health")]
Health = NodePath("../../Brick Player/HealthComponent")
[node name="DeathScreen" parent="UI Layer" index="1"]
CurrentLevel = ExtResource("17_m6h4x")
[node name="Marketplace" parent="UI Layer" index="3" node_paths=PackedStringArray("SkillUnlockerComponent", "ComponentsToDisable")]
SkillUnlockerComponent = NodePath("../../Brick Player/SkillUnlockerComponent")
ComponentsToDisable = [NodePath("../../Brick Player")]
[node name="Tooltip" parent="." instance=ExtResource("18_l3a7y")]
position = Vector2(1016, -104)
Text = "WIP"
[node name="Tooltip2" parent="." instance=ExtResource("18_l3a7y")]
position = Vector2(1576, -40)
Text = "WIP"
[node name="WorldEnvironment" parent="." instance=ExtResource("20_embdf")]
@@ -354,6 +365,9 @@ position = Vector2(1359, -42)
[node name="Killzone" parent="." instance=ExtResource("21_p30ax")]
position = Vector2(2456, 815)
[node name="SkillPickup" parent="." instance=ExtResource("24_6fdf4")]
position = Vector2(1136, -109)
[connection signal="Death" from="Brick Player/HealthComponent" to="UI Layer/DeathScreen" method="OnPlayerDeath"]
[connection signal="Death" from="Brick Player/HealthComponent" to="UI Layer/GameOverScreen" method="OnPlayerDeath"]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

123
scenes/level_village_6.tscn Normal file

File diff suppressed because one or more lines are too long

25
scripts/Constants.cs Normal file
View File

@@ -0,0 +1,25 @@
namespace Mr.BrickAdventures;
/// <summary>
/// Constants for autoload paths and other commonly used values.
/// </summary>
public static class Constants
{
// Autoload paths
public const string EventBusPath = "/root/EventBus";
public const string GameManagerPath = "/root/GameManager";
public const string GameStateStorePath = "/root/GameStateStore";
public const string SaveSystemPath = "/root/SaveSystem";
public const string SpeedRunManagerPath = "/root/SpeedRunManager";
public const string GhostManagerPath = "/root/GhostManager";
public const string AchievementManagerPath = "/root/AchievementManager";
public const string StatisticsManagerPath = "/root/StatisticsManager";
public const string SkillManagerPath = "/root/SkillManager";
public const string FloatingTextManagerPath = "/root/FloatingTextManager";
public const string UIManagerPath = "/root/UIManager";
public const string ConsoleManagerPath = "/root/ConsoleManager";
public const string ConfigFileHandlerPath = "/root/ConfigFileHandler";
// Group names
public const string CoinsGroup = "coins";
}

1
scripts/Constants.cs.uid Normal file
View File

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

View File

@@ -0,0 +1,29 @@
using Godot;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.Events;
/// <summary>
/// Handles coin collection events and updates the GameStateStore.
/// Replaces the manual coin logic in GameManager.
/// </summary>
public partial class CoinStateHandler : Node
{
public override void _Ready()
{
EventBus.Instance.CoinCollected += OnCoinCollected;
}
public override void _ExitTree()
{
if (EventBus.Instance != null)
{
EventBus.Instance.CoinCollected -= OnCoinCollected;
}
}
private void OnCoinCollected(int amount, Vector2 position)
{
GameStateStore.Instance?.AddSessionCoins(amount);
}
}

View File

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

View File

@@ -1,4 +1,5 @@
using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.Events;
@@ -10,11 +11,10 @@ public partial class GhostEventHandler : Node
public override void _Ready()
{
_ghostManager = GetNode<GhostManager>("/root/GhostManager");
var eventBus = GetNode<EventBus>("/root/EventBus");
_ghostManager = GetNode<GhostManager>(Constants.GhostManagerPath);
eventBus.LevelStarted += OnLevelStarted;
eventBus.LevelCompleted += OnLevelCompleted;
EventBus.Instance.LevelStarted += OnLevelStarted;
EventBus.Instance.LevelCompleted += OnLevelCompleted;
}
private void OnLevelStarted(int levelIndex, Node currentScene)

View File

@@ -0,0 +1,39 @@
using Godot;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.Events;
/// <summary>
/// Handles level completion events and updates GameStateStore.
/// </summary>
public partial class LevelStateHandler : Node
{
public override void _Ready()
{
EventBus.Instance.LevelCompleted += OnLevelCompleted;
}
public override void _ExitTree()
{
if (EventBus.Instance != null)
{
EventBus.Instance.LevelCompleted -= OnLevelCompleted;
}
}
private void OnLevelCompleted(int levelIndex, Node currentScene, double completionTime)
{
var store = GameStateStore.Instance;
if (store == null) return;
// Mark level complete and unlock next
store.MarkLevelComplete(levelIndex);
// Commit session data to persistent state
store.CommitSessionCoins();
store.CommitSessionSkills();
// Reset session for next level
store.ResetSession();
}
}

View File

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

View File

@@ -0,0 +1,29 @@
using Godot;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.Events;
/// <summary>
/// Handles player death events and updates lives in GameStateStore.
/// </summary>
public partial class LivesStateHandler : Node
{
public override void _Ready()
{
EventBus.Instance.PlayerDied += OnPlayerDied;
}
public override void _ExitTree()
{
if (EventBus.Instance != null)
{
EventBus.Instance.PlayerDied -= OnPlayerDied;
}
}
private void OnPlayerDied(Vector2 position)
{
GameStateStore.Instance?.RemoveLife();
GameStateStore.Instance?.ResetSession();
}
}

View File

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

View File

@@ -0,0 +1,43 @@
using Godot;
using Mr.BrickAdventures.Autoloads;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.Events;
/// <summary>
/// Handles skill collection events and unlocks skills via GameStateStore.
/// Skills are immediately activated but only persisted on level complete.
/// </summary>
public partial class SkillCollectHandler : Node
{
private SkillManager _skillManager;
public override void _Ready()
{
_skillManager = SkillManager.Instance;
EventBus.Instance.SkillCollected += OnSkillCollected;
}
public override void _ExitTree()
{
if (EventBus.Instance != null)
{
EventBus.Instance.SkillCollected -= OnSkillCollected;
}
}
private void OnSkillCollected(SkillData skill, Vector2 position)
{
if (skill == null) return;
// Unlock in session (will be committed on level complete, lost on death)
GameStateStore.Instance?.UnlockSkillInSession(skill);
// Immediately activate the skill for the player
skill.Level = 1;
_skillManager?.AddSkill(skill);
// Emit skill unlocked event for UI/achievements
EventBus.EmitSkillUnlocked(skill.Name, skill.Level);
}
}

View File

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

View File

@@ -1,4 +1,5 @@
using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.Events;
@@ -10,10 +11,9 @@ public partial class SpeedRunEventHandler : Node
public override void _Ready()
{
_speedRunManager = GetNode<SpeedRunManager>("/root/SpeedRunManager");
var eventBus = GetNode<EventBus>("/root/EventBus");
_speedRunManager = GetNode<SpeedRunManager>(Constants.SpeedRunManagerPath);
eventBus.LevelCompleted += OnLevelCompleted;
EventBus.Instance.LevelCompleted += OnLevelCompleted;
}
private void OnLevelCompleted(int levelIndex, Node currentScene, double completionTime)

View File

@@ -0,0 +1,62 @@
using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.Events;
/// <summary>
/// Handles game events and updates statistics accordingly.
/// Listens to EventBus signals and increments relevant stats.
/// </summary>
public partial class StatisticsEventHandler : Node
{
private StatisticsManager _statisticsManager;
public override void _Ready()
{
_statisticsManager = GetNode<StatisticsManager>(Constants.StatisticsManagerPath);
// Subscribe to events
EventBus.Instance.CoinCollected += OnCoinCollected;
EventBus.Instance.EnemyDefeated += OnEnemyDefeated;
EventBus.Instance.PlayerDied += OnPlayerDied;
EventBus.Instance.LevelCompleted += OnLevelCompleted;
EventBus.Instance.ChildRescued += OnChildRescued;
}
public override void _ExitTree()
{
if (EventBus.Instance == null) return;
EventBus.Instance.CoinCollected -= OnCoinCollected;
EventBus.Instance.EnemyDefeated -= OnEnemyDefeated;
EventBus.Instance.PlayerDied -= OnPlayerDied;
EventBus.Instance.LevelCompleted -= OnLevelCompleted;
EventBus.Instance.ChildRescued -= OnChildRescued;
}
private void OnCoinCollected(int amount, Vector2 position)
{
_statisticsManager.IncrementStat("coins_collected", amount);
}
private void OnEnemyDefeated(Node enemy, Vector2 position)
{
_statisticsManager.IncrementStat("enemies_defeated");
}
private void OnPlayerDied(Vector2 position)
{
_statisticsManager.IncrementStat("deaths");
}
private void OnLevelCompleted(int levelIndex, Node currentScene, double completionTime)
{
_statisticsManager.IncrementStat("levels_completed");
}
private void OnChildRescued(Vector2 position)
{
_statisticsManager.IncrementStat("children_rescued");
}
}

View File

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

View File

@@ -6,4 +6,9 @@ public partial class CollectableResource : Resource
{
[Export] public float Amount { get; set; } = 0.0f;
[Export] public CollectableType Type { get; set; }
/// <summary>
/// The skill to unlock when collected. Only used when Type is Skill.
/// </summary>
[Export] public SkillData Skill { get; set; }
}

View File

@@ -2,7 +2,9 @@ namespace Mr.BrickAdventures.scripts.Resources;
public enum CollectableType
{
None, // when no collectable type is specified
Coin,
Kid,
Health,
Skill,
}

View File

@@ -10,7 +10,6 @@ public partial class SkillData : Resource
[Export] public string Name { get; set; } = "New Skill";
[Export] public string Description { get; set; } = "New Skill";
[Export] public Texture2D Icon { get; set; }
[Export] public bool IsActive { get; set; } = false;
[Export] public int Level { get; set; } = 1;
[Export] public SkillType Type { get; set; } = SkillType.Throw;
[Export] public PackedScene Node { get; set; }

View File

@@ -0,0 +1,73 @@
using System.Collections.Generic;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.State;
/// <summary>
/// Persistent player data that survives across sessions.
/// This is a POCO (Plain Old C# Object) for predictable state management.
/// </summary>
public class PlayerState
{
/// <summary>
/// Saved coins (not including current session).
/// </summary>
public int Coins { get; set; }
/// <summary>
/// Remaining lives.
/// </summary>
public int Lives { get; set; } = 3;
/// <summary>
/// Indices of completed levels.
/// </summary>
public List<int> CompletedLevels { get; set; } = new();
/// <summary>
/// Indices of levels the player can access.
/// </summary>
public List<int> UnlockedLevels { get; set; } = new() { 0 };
/// <summary>
/// Skills the player has permanently unlocked.
/// </summary>
public List<SkillData> UnlockedSkills { get; set; } = new();
/// <summary>
/// Statistics dictionary for tracking game stats.
/// </summary>
public Dictionary<string, int> Statistics { get; set; } = new();
/// <summary>
/// IDs of unlocked achievements.
/// </summary>
public List<string> UnlockedAchievements { get; set; } = new();
/// <summary>
/// Creates a fresh default player state.
/// </summary>
public static PlayerState CreateDefault() => new()
{
Coins = 0,
Lives = 3,
CompletedLevels = new List<int>(),
UnlockedLevels = new List<int> { 0 },
UnlockedSkills = new List<SkillData>(),
Statistics = new Dictionary<string, int>()
};
/// <summary>
/// Resets this state to default values.
/// </summary>
public void Reset()
{
Coins = 0;
Lives = 3;
CompletedLevels.Clear();
UnlockedLevels.Clear();
UnlockedLevels.Add(0);
UnlockedSkills.Clear();
Statistics.Clear();
}
}

View File

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

View File

@@ -0,0 +1,55 @@
using System.Collections.Generic;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.State;
/// <summary>
/// Data for the current gameplay session.
/// Reset when player dies or completes a level.
/// </summary>
public class SessionState
{
/// <summary>
/// Current level index being played.
/// </summary>
public int CurrentLevel { get; set; }
/// <summary>
/// Coins collected during this session (not yet saved).
/// </summary>
public int CoinsCollected { get; set; }
/// <summary>
/// Skills unlocked during this session (not yet saved).
/// </summary>
public List<SkillData> SkillsUnlocked { get; set; } = new();
/// <summary>
/// Creates a fresh session state.
/// </summary>
public static SessionState CreateDefault() => new()
{
CurrentLevel = 0,
CoinsCollected = 0,
SkillsUnlocked = new List<SkillData>()
};
/// <summary>
/// Resets session state to defaults.
/// </summary>
public void Reset()
{
CoinsCollected = 0;
SkillsUnlocked.Clear();
}
/// <summary>
/// Resets completely including level.
/// </summary>
public void ResetAll()
{
CurrentLevel = 0;
CoinsCollected = 0;
SkillsUnlocked.Clear();
}
}

View File

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

View File

@@ -1,4 +1,5 @@
using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.UI;
@@ -16,8 +17,8 @@ public partial class AudioSettings : Control
public override void _Ready()
{
_uiManager = GetNode<UIManager>("/root/UIManager");
_configFileHandler = GetNode<ConfigFileHandler>("/root/ConfigFileHandler");
_uiManager = GetNode<UIManager>(Constants.UIManagerPath);
_configFileHandler = GetNode<ConfigFileHandler>(Constants.ConfigFileHandlerPath);
Initialize();
MasterVolumeSlider.ValueChanged += OnMasterVolumeChanged;
MusicVolumeSlider.ValueChanged += OnMusicVolumeChanged;

View File

@@ -1,4 +1,5 @@
using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
using Mr.BrickAdventures.scripts.components;
using Mr.BrickAdventures.scripts.Resources;
@@ -17,7 +18,7 @@ public partial class ChargeProgressBar : ProgressBar
{
ProgressBar.Hide();
_skillManager = GetNodeOrNull<SkillManager>("/root/SkillManager");
_skillManager = GetNodeOrNull<SkillManager>(Constants.SkillManagerPath);
if (_skillManager == null)
{
GD.PrintErr("ChargeProgressBar: SkillManager autoload not found.");

View File

@@ -1,4 +1,5 @@
using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.UI;
@@ -9,7 +10,7 @@ public partial class Credits : Control
public override void _Ready()
{
_uiManager = GetNode<UIManager>("/root/UIManager");
_uiManager = GetNode<UIManager>(Constants.UIManagerPath);
}
public override void _UnhandledInput(InputEvent @event)

View File

@@ -1,6 +1,8 @@
using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
using Mr.BrickAdventures.scripts.Resources;
using Mr.BrickAdventures.scripts.State;
namespace Mr.BrickAdventures.scripts.UI;
@@ -18,19 +20,36 @@ public partial class DeathScreen : Control
public override void _Ready()
{
_gameManager = GetNode<GameManager>("/root/GameManager");
SetLabels();
_gameManager = GameManager.Instance;
// Subscribe to lives changed event for reactive updates
EventBus.Instance.LivesChanged += OnLivesChanged;
}
public override void _ExitTree()
{
if (EventBus.Instance != null)
{
EventBus.Instance.LivesChanged -= OnLivesChanged;
}
}
private void OnLivesChanged(int lives)
{
// Update the label when lives change
LivesLeftLabel.Text = $" x {lives}";
}
private void SetLabels()
{
if (_gameManager == null) return;
if (CurrentLevel != null)
{
CurrentLevelLabel.Text = CurrentLevel.LevelName;
}
LivesLeftLabel.Text = $" x {_gameManager.GetLives()}";
// Read current lives from store
var lives = GameStateStore.Instance?.Player.Lives ?? 0;
LivesLeftLabel.Text = $" x {lives}";
}
private void SetupTimer()
@@ -65,7 +84,8 @@ public partial class DeathScreen : Control
private void OnTimeout()
{
if (_gameManager == null || _gameManager.GetLives() == 0) return;
var lives = GameStateStore.Instance?.Player.Lives ?? 0;
if (lives == 0) return;
GetTree().ReloadCurrentScene();
}

View File

@@ -1,4 +1,5 @@
using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.UI;
@@ -14,7 +15,7 @@ public partial class GameOverScreen : Control
public override void _Ready()
{
_gameManager = GetNode<GameManager>("/root/GameManager");
_gameManager = GameManager.Instance;
RestartButton.Pressed += OnRestartClicked;
MainMenuButton.Pressed += OnMainMenuClicked;
}

View File

@@ -1,4 +1,5 @@
using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
using Mr.BrickAdventures.scripts.components;
@@ -15,7 +16,7 @@ public partial class Hud : Control
public override void _Ready()
{
_gameManager = GetNode<GameManager>("/root/GameManager");
_gameManager = GameManager.Instance;
}
public override void _Process(double delta)

View File

@@ -1,4 +1,5 @@
using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.UI;
@@ -21,9 +22,9 @@ public partial class MainMenu : Control
public override void _Ready()
{
_saveSystem = GetNode<SaveSystem>("/root/SaveSystem");
_gameManager = GetNode<GameManager>("/root/GameManager");
_uiManager = GetNode<UIManager>("/root/UIManager");
_saveSystem = SaveSystem.Instance;
_gameManager = GameManager.Instance;
_uiManager = GetNode<UIManager>(Constants.UIManagerPath);
NewGameButton.Pressed += OnNewGamePressed;
ContinueButton.Pressed += OnContinuePressed;

View File

@@ -1,6 +1,7 @@
using System.Collections.Generic;
using Godot;
using Godot.Collections;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
using Mr.BrickAdventures.scripts.components;
using Mr.BrickAdventures.scripts.Resources;
@@ -25,9 +26,8 @@ public partial class Marketplace : Control
public override void _Ready()
{
_gameManager = GetNode<GameManager>("/root/GameManager");
_skillManager = GetNode<SkillManager>("/root/SkillManager");
_skillManager.SkillRemoved += OnSkillRemoved;
_gameManager = GameManager.Instance;
_skillManager = SkillManager.Instance;
Skills = _skillManager.AvailableSkills;
@@ -41,11 +41,16 @@ public partial class Marketplace : Control
foreach (var skill in unlockedSkills) CreateSkillButton(skill);
SkillUnlockerComponent.SkillUnlocked += OnSkillUnlocked;
EventBus.Instance.SkillCollected += OnGlobalSkillCollected;
}
public override void _ExitTree()
{
SkillUnlockerComponent.SkillUnlocked -= OnSkillUnlocked;
if (EventBus.Instance != null)
{
EventBus.Instance.SkillCollected -= OnGlobalSkillCollected;
}
}
public override void _Input(InputEvent @event)
@@ -80,7 +85,7 @@ public partial class Marketplace : Control
foreach (var btn in _skillButtons)
{
if (btn.Data.IsActive) btn.Activate();
if (_skillManager.IsSkillActive(btn.Data)) btn.Activate();
else btn.Deactivate();
}
}
@@ -117,7 +122,7 @@ public partial class Marketplace : Control
if (skill.Level < skill.MaxLevel)
{
SkillUnlockerComponent.TryUpgradeSkill(skill);
if (!skill.IsActive) SkillUnlockerComponent.SkillManager.ToggleSkillActivation(skill);
if (!SkillUnlockerComponent.SkillManager.IsSkillActive(skill)) SkillUnlockerComponent.SkillManager.ToggleSkillActivation(skill);
}
else
{
@@ -136,28 +141,15 @@ public partial class Marketplace : Control
foreach (var btn in _skillButtons)
{
if (btn.Data.IsActive)
if (SkillUnlockerComponent.SkillManager.IsSkillActive(btn.Data))
btn.Activate();
else
btn.Deactivate();
}
}
private void OnSkillRemoved(SkillData skill)
private void OnGlobalSkillCollected(SkillData skill, Vector2 position)
{
SkillButton buttonToRemove = null;
foreach (var button in _skillButtons)
{
if (button.Data == skill)
{
buttonToRemove = button;
break;
}
}
if (buttonToRemove != null)
{
_skillButtons.Remove(buttonToRemove);
buttonToRemove.QueueFree();
}
OnSkillUnlocked(skill);
}
}

View File

@@ -1,4 +1,5 @@
using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
using Mr.BrickAdventures.scripts.components;
using Mr.BrickAdventures.scripts.Resources;
@@ -18,7 +19,7 @@ public partial class MarketplaceButton : Button
public override void _Ready()
{
_gameManager = GetNode<GameManager>("/root/GameManager");
_gameManager = GameManager.Instance;
var player = _gameManager.Player;
if (player == null) return;
@@ -28,7 +29,7 @@ public partial class MarketplaceButton : Button
_skillUnlockerComponent.SkillUnlocked += OnSkillStateChanged;
}
_skillManager = GetNode<SkillManager>("/root/SkillManager");
_skillManager = SkillManager.Instance;
_skillManager.SkillRemoved += OnSkillStateChanged;
UpdateButtonState();

View File

@@ -1,4 +1,5 @@
using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.UI;
@@ -18,8 +19,8 @@ public partial class PauseMenu : Control
public override void _Ready()
{
_gameManager = GetNode<GameManager>("/root/GameManager");
_uiManager = GetNode<UIManager>("/root/UIManager");
_gameManager = GameManager.Instance;
_uiManager = GetNode<UIManager>(Constants.UIManagerPath);
ResumeButton.Pressed += OnResumePressed;
MainMenuButton.Pressed += OnMainMenuPressed;

View File

@@ -1,4 +1,5 @@
using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.UI;
@@ -19,7 +20,7 @@ public partial class SettingsMenu : Control
public override void _Ready()
{
_uiManager = GetNode<UIManager>("/root/UIManager");
_uiManager = GetNode<UIManager>(Constants.UIManagerPath);
InputSettingsButton.Pressed += OnInputSettingsPressed;
AudioSettingsButton.Pressed += OnAudioSettingsPressed;

View File

@@ -1,4 +1,5 @@
using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.UI;
@@ -12,7 +13,7 @@ public partial class SpeedRunHud : Control
public override void _Ready()
{
_speedRunManager = GetNode<SpeedRunManager>("/root/SpeedRunManager");
_speedRunManager = GetNode<SpeedRunManager>(Constants.SpeedRunManagerPath);
_speedRunManager.TimeUpdated += OnTimerUpdated;

View File

@@ -5,26 +5,27 @@ using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components;
[GlobalClass]
public partial class BrickArmorSkillComponent : Node, ISkill
public partial class BrickArmorSkillComponent : SkillComponentBase
{
private HealthComponent _healthComponent;
private SkillData _skillData;
private float _armorBonus = 0;
public void Initialize(Node owner, SkillData data)
public override void Initialize(Node owner, SkillData data)
{
if (owner is not PlayerController player) return;
_healthComponent = player.GetNode<HealthComponent>("HealthComponent");
_skillData = data;
base.Initialize(owner, data);
if (Player != null)
{
_healthComponent = Player.GetNode<HealthComponent>("HealthComponent");
}
}
public void Activate()
public override void Activate()
{
if (_healthComponent == null || _skillData == null) return;
ApplyUpgrade(_skillData.Upgrades[_skillData.Level - 1]);
if (_healthComponent == null || Data == null) return;
ApplyUpgrade(Data.Upgrades[Data.Level - 1]);
}
public void Deactivate()
public override void Deactivate()
{
if (_healthComponent == null) return;
_healthComponent.MaxHealth -= _armorBonus;
@@ -35,7 +36,7 @@ public partial class BrickArmorSkillComponent : Node, ISkill
_armorBonus = 0;
}
public void ApplyUpgrade(SkillUpgrade upgrade)
public override void ApplyUpgrade(SkillUpgrade upgrade)
{
if (_healthComponent == null || upgrade == null) return;

View File

@@ -1,4 +1,5 @@
using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
using Mr.BrickAdventures.scripts.interfaces;
using Mr.BrickAdventures.scripts.Resources;
@@ -6,41 +7,33 @@ using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components;
[GlobalClass]
public partial class BrickShieldSkillComponent : Node, ISkill
public partial class BrickShieldSkillComponent : SkillComponentBase
{
[Export] public PackedScene ShieldScene { get; set; }
private PlayerController _player;
private Node2D _shieldInstance;
private SkillData _skillData;
private GameManager _gameManager;
private SkillManager _skillManager;
private HealthComponent _shieldHealth;
public override void _Ready()
{
_gameManager = GetNode<GameManager>("/root/GameManager");
_skillManager = GetNode<SkillManager>("/root/SkillManager");
_gameManager = GameManager.Instance;
_skillManager = SkillManager.Instance;
}
public void Initialize(Node owner, SkillData data)
public override void Activate()
{
_player = owner as PlayerController;
_skillData = data;
}
public void Activate()
{
if (_player == null || ShieldScene == null || _shieldInstance != null) return;
if (Player == null || ShieldScene == null || _shieldInstance != null) return;
_shieldInstance = ShieldScene.Instantiate<Node2D>();
_player.AddChild(_shieldInstance);
Player.AddChild(_shieldInstance);
_shieldInstance.Position = Vector2.Zero;
_shieldInstance.TreeExiting += OnShieldDestroyed;
_shieldHealth = _shieldInstance.GetNode<HealthComponent>("HealthComponent");
}
public void Deactivate()
public override void Deactivate()
{
if (_shieldInstance != null && IsInstanceValid(_shieldInstance))
{
@@ -50,7 +43,7 @@ public partial class BrickShieldSkillComponent : Node, ISkill
_shieldInstance = null;
}
public void ApplyUpgrade(SkillUpgrade upgrade)
public override void ApplyUpgrade(SkillUpgrade upgrade)
{
upgrade.Properties.TryGetValue("shield_health", out var newHealth);
if (_shieldHealth != null)
@@ -62,10 +55,10 @@ public partial class BrickShieldSkillComponent : Node, ISkill
private void OnShieldDestroyed()
{
if (_gameManager != null && _skillData != null && _skillManager != null)
if (_gameManager != null && Data != null && _skillManager != null)
{
_gameManager.RemoveSkill(_skillData.Name);
_skillManager.RemoveSkill(_skillData.Name);
_gameManager.RemoveSkill(Data.Name);
_skillManager.RemoveSkill(Data.Name);
}
_shieldInstance = null;
}

View File

@@ -5,16 +5,14 @@ using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components;
[GlobalClass]
public partial class BrickThrowComponent : Node, ISkill
public partial class BrickThrowComponent : SkillComponentBase
{
[Export] public PackedScene BrickScene { get; set; }
[Export] public float FireRate { get; set; } = 1.0f;
[Export] public PlayerController PlayerController { get; set; }
[Export] public ThrowInputResource ThrowInputBehavior { get; set; }
private bool _canThrow = true;
private Timer _timer;
private SkillData _skillData;
public override void _Ready()
{
@@ -59,7 +57,7 @@ public partial class BrickThrowComponent : Node, ISkill
private void ThrowBrick(float powerMultiplier = 1f)
{
if (!_canThrow || PlayerController == null || BrickScene == null)
if (!_canThrow || Player == null || BrickScene == null)
return;
var instance = BrickScene.Instantiate<Node2D>();
@@ -69,9 +67,9 @@ public partial class BrickThrowComponent : Node, ISkill
{
var @params = new ProjectileInitParams()
{
Position = PlayerController.GlobalPosition,
Rotation = PlayerController.Rotation,
Direction = PlayerController.LastDirection,
Position = Player.GlobalPosition,
Rotation = Player.Rotation,
Direction = Player.LastDirection,
PowerMultiplier = powerMultiplier,
};
@@ -83,36 +81,30 @@ public partial class BrickThrowComponent : Node, ISkill
_timer.Start();
}
public void Initialize(Node owner, SkillData data)
public override void Initialize(Node owner, SkillData data)
{
PlayerController = owner as PlayerController;
_skillData = data;
base.Initialize(owner, data);
ThrowInputBehavior = (ThrowInputResource)ThrowInputBehavior?.Duplicate();
if (PlayerController == null)
if (Data.Level > 0 && Data.Upgrades.Count >= Data.Level)
{
GD.PushError("BrickThrowComponent: Owner is not a PlayerController.");
}
if (_skillData.Level > 0 && _skillData.Upgrades.Count >= _skillData.Level)
{
ApplyUpgrade(_skillData.Upgrades[_skillData.Level - 1]);
ApplyUpgrade(Data.Upgrades[Data.Level - 1]);
}
}
public void Activate()
public override void Activate()
{
if (ThrowInputBehavior != null) ThrowInputBehavior.ThrowRequested += ThrowBrick;
SetProcessInput(true);
}
public void Deactivate()
public override void Deactivate()
{
if (ThrowInputBehavior != null) ThrowInputBehavior.ThrowRequested -= ThrowBrick;
}
public void ApplyUpgrade(SkillUpgrade upgrade)
public override void ApplyUpgrade(SkillUpgrade upgrade)
{
foreach (var property in upgrade.Properties)
{

View File

@@ -0,0 +1,46 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
[GlobalClass]
public partial class CleanupOnCollisionComponent : Node
{
[Export(PropertyHint.Range, "0, 5, 0.1")] public float CleanupDelay { get; set; } = 0.5f;
private RigidBody2D _body;
private CleanupComponent _cleanupComponent;
private bool _isCleaningUp = false;
public override void _Ready()
{
_body = Owner as RigidBody2D;
if (_body == null)
{
GD.PrintErr("CleanupOnCollisionComponent must be attached to a RigidBody2D.");
SetProcess(false);
return;
}
_cleanupComponent = Owner.GetNode<CleanupComponent>("CleanupComponent");
if (_cleanupComponent == null)
{
GD.PrintErr("CleanupOnCollisionComponent requires a CleanupComponent on the same node.");
SetProcess(false);
return;
}
_body.BodyEntered += (_) => OnBodyEntered();
}
private async void OnBodyEntered()
{
if (_isCleaningUp) return;
_isCleaningUp = true;
await ToSignal(GetTree().CreateTimer(CleanupDelay), Timer.SignalName.Timeout);
_cleanupComponent?.CleanUp();
}
}

View File

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

View File

@@ -1,5 +1,6 @@
using System;
using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
using Mr.BrickAdventures.scripts.Resources;
@@ -17,7 +18,11 @@ public partial class CollectableComponent : Node
[Signal] public delegate void CollectedEventHandler(float amount, CollectableType type, Node2D body);
private FloatingTextManager _floatingTextManager;
/// <summary>
/// Delegate for checking if collection should be allowed.
/// Return false to prevent collection.
/// </summary>
public Func<Node2D, bool> CanCollect { get; set; }
public override void _Ready()
{
@@ -28,8 +33,6 @@ public partial class CollectableComponent : Node
if (Owner.HasNode("FadeAwayComponent"))
_hasFadeAway = true;
_floatingTextManager = GetNode<FloatingTextManager>("/root/FloatingTextManager");
}
private async void OnArea2DBodyEntered(Node2D body)
@@ -38,18 +41,34 @@ public partial class CollectableComponent : Node
{
if (!body.HasNode("CanPickUpComponent")) return;
// Allow components to veto collection (e.g., full health for potions)
if (CanCollect != null && !CanCollect(body)) return;
if (Owner is Node2D ownerNode)
{
switch (Data.Type)
{
case CollectableType.Coin:
_floatingTextManager?.ShowCoin((int)Data.Amount, ownerNode.GlobalPosition);
FloatingTextManager.Instance?.ShowCoin((int)Data.Amount, ownerNode.GlobalPosition);
EventBus.EmitCoinCollected((int)Data.Amount, ownerNode.GlobalPosition);
break;
case CollectableType.Health:
_floatingTextManager?.ShowMessage("Healed!", ownerNode.GlobalPosition);
FloatingTextManager.Instance?.ShowMessage("Healed!", ownerNode.GlobalPosition);
EventBus.EmitItemCollected(Data.Type, Data.Amount, ownerNode.GlobalPosition);
break;
case CollectableType.Kid:
_floatingTextManager?.ShowMessage("Rescued!", ownerNode.GlobalPosition);
FloatingTextManager.Instance?.ShowMessage("Rescued!", ownerNode.GlobalPosition);
EventBus.EmitChildRescued(ownerNode.GlobalPosition);
break;
case CollectableType.Skill:
if (Data.Skill != null)
{
FloatingTextManager.Instance?.ShowMessage($"{Data.Skill.Name} Unlocked!", ownerNode.GlobalPosition);
EventBus.EmitSkillCollected(Data.Skill, ownerNode.GlobalPosition);
}
break;
default:
EventBus.EmitItemCollected(Data.Type, Data.Amount, ownerNode.GlobalPosition);
break;
}
}

View File

@@ -6,40 +6,25 @@ using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components;
[GlobalClass]
public partial class DoubleJumpSkillComponent : Node, ISkill
public partial class DoubleJumpSkillComponent : SkillComponentBase
{
[Export] private PackedScene _doubleJumpAbilityScene;
private PlayerController _playerController;
public void Initialize(Node owner, SkillData data)
public override void Activate()
{
_playerController = owner as PlayerController;
if (_playerController == null)
{
GD.PrintErr("DoubleJumpSkillComponent must be a child of a PlayerController.");
}
}
if (Player == null) return;
public void Activate()
{
if (_playerController == null) return;
var hasAbility = _playerController.GetActiveAbilities().Any(ability => ability is DoubleJumpAbility);
var hasAbility = Player.GetActiveAbilities().Any(ability => ability is DoubleJumpAbility);
if (!hasAbility)
{
var abilityInstance = _doubleJumpAbilityScene.Instantiate<DoubleJumpAbility>();
_playerController.AddAbility(abilityInstance);
Player.AddAbility(abilityInstance);
}
}
public void Deactivate()
public override void Deactivate()
{
_playerController?.RemoveAbility<DoubleJumpAbility>();
}
public void ApplyUpgrade(SkillUpgrade upgrade)
{
Player?.RemoveAbility<DoubleJumpAbility>();
}
}

View File

@@ -1,5 +1,6 @@
using System.Threading.Tasks;
using Godot;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.components;
@@ -34,6 +35,12 @@ public partial class EnemyDeathComponent : Node
private async Task Die()
{
// Emit enemy defeated event for statistics and other systems
if (Owner is Node2D ownerNode)
{
EventBus.EmitEnemyDefeated(Owner, ownerNode.GlobalPosition);
}
CollisionShape.SetDisabled(true);
var tween = CreateTween();
tween.TweenProperty(Owner, "scale", Vector2.Zero, TweenDuration);

View File

@@ -1,6 +1,8 @@
using Godot;
using Mr.BrickAdventures;
using Mr.BrickAdventures.Autoloads;
using Mr.BrickAdventures.scripts.interfaces;
using Mr.BrickAdventures.scripts.State;
namespace Mr.BrickAdventures.scripts.components;
@@ -20,8 +22,8 @@ public partial class ExitDoorComponent : Area2D, IUnlockable
public override void _Ready()
{
_gameManager = GetNode<GameManager>("/root/GameManager");
_achievementManager = GetNode<AchievementManager>("/root/AchievementManager");
_gameManager = GameManager.Instance;
_achievementManager = GetNode<AchievementManager>(Constants.AchievementManagerPath);
BodyEntered += OnExitAreaBodyEntered;
@@ -33,7 +35,9 @@ public partial class ExitDoorComponent : Area2D, IUnlockable
EmitSignalExitTriggered();
_achievementManager.UnlockAchievement(AchievementId);
_gameManager.UnlockLevel((int)_gameManager.PlayerState["current_level"] + 1);
// Get current level from GameStateStore
var currentLevel = GameStateStore.Instance?.Session.CurrentLevel ?? 0;
_gameManager.UnlockLevel(currentLevel + 1);
CallDeferred(nameof(GoToNextLevel));
}

View File

@@ -0,0 +1,47 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
[GlobalClass]
public partial class FallOnDetectionComponent : Node2D
{
[Export] public Area2D DetectionArea { get; set; }
[Export] public RigidBody2D TargetBody { get; set; }
[Export] public float FallDelay { get; set; } = 0.2f;
private bool _hasTriggered = false;
public override void _Ready()
{
if (DetectionArea == null)
{
GD.PrintErr("FallOnDetectionComponent: DetectionArea is not set.");
SetProcess(false);
return;
}
if (TargetBody == null)
{
GD.PrintErr("FallOnDetectionComponent: TargetBody is not set.");
SetProcess(false);
return;
}
DetectionArea.BodyEntered += OnBodyEntered;
}
private async void OnBodyEntered(Node2D body)
{
if (_hasTriggered) return;
_hasTriggered = true;
if (FallDelay > 0)
{
await ToSignal(GetTree().CreateTimer(FallDelay), Timer.SignalName.Timeout);
}
if (IsInstanceValid(TargetBody))
{
TargetBody.GravityScale = 1.0f;
}
}
}

Some files were not shown because too many files have changed in this diff Show More