Compare commits
8 Commits
9c74a1ffb5
...
clean-code
Author | SHA1 | Date | |
---|---|---|---|
817bd96433 | |||
2cc54f7b37 | |||
406036504a | |||
173f0e5703 | |||
d84f7d1740 | |||
2ad0fe26d2 | |||
c4f7be1b10 | |||
b957b56567 |
27
Autoloads/ConfigFileHandler.cs
Normal file
27
Autoloads/ConfigFileHandler.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using Godot;
|
||||
|
||||
namespace Mr.BrickAdventures.Autoloads;
|
||||
|
||||
public partial class ConfigFileHandler : Node
|
||||
{
|
||||
private ConfigFile _settingsConfig = new();
|
||||
public const string SettingsPath = "user://settings.ini";
|
||||
|
||||
public ConfigFile SettingsConfig => _settingsConfig;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
if (!FileAccess.FileExists(SettingsPath))
|
||||
{
|
||||
var err = _settingsConfig.Save(SettingsPath);
|
||||
if (err != Error.Ok)
|
||||
GD.PushError($"Failed to create settings file at {SettingsPath}: {err}");
|
||||
}
|
||||
else
|
||||
{
|
||||
var err = _settingsConfig.Load(SettingsPath);
|
||||
if (err != Error.Ok)
|
||||
GD.PushError($"Failed to load settings file at {SettingsPath}: {err}");
|
||||
}
|
||||
}
|
||||
}
|
1
Autoloads/ConfigFileHandler.cs.uid
Normal file
1
Autoloads/ConfigFileHandler.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://8cyvbeyd13cj
|
241
Autoloads/GameManager.cs
Normal file
241
Autoloads/GameManager.cs
Normal file
@@ -0,0 +1,241 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using Mr.BrickAdventures.scripts.components;
|
||||
using Mr.BrickAdventures.scripts.Resources;
|
||||
|
||||
namespace Mr.BrickAdventures.Autoloads;
|
||||
|
||||
public partial class GameManager : Node
|
||||
{
|
||||
[Export] public Array<PackedScene> LevelScenes { get; set; } = new();
|
||||
|
||||
public PlayerController Player { get; set; }
|
||||
|
||||
private List<Node> _sceneNodes = new();
|
||||
|
||||
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>() }
|
||||
};
|
||||
|
||||
public Dictionary CurrentSessionState { get; private set; } = new()
|
||||
{
|
||||
{ "coins_collected", 0 },
|
||||
{ "skills_unlocked", new Array<SkillData>() }
|
||||
};
|
||||
|
||||
public override void _EnterTree()
|
||||
{
|
||||
GetTree().NodeAdded += OnNodeAdded;
|
||||
GetTree().NodeRemoved += OnNodeRemoved;
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
GetTree().NodeAdded -= OnNodeAdded;
|
||||
GetTree().NodeRemoved -= OnNodeRemoved;
|
||||
_sceneNodes.Clear();
|
||||
}
|
||||
|
||||
private void OnNodeAdded(Node node)
|
||||
{
|
||||
_sceneNodes.Add(node);
|
||||
}
|
||||
|
||||
private void OnNodeRemoved(Node node)
|
||||
{
|
||||
_sceneNodes.Remove(node);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
var sessionCoins = (int)CurrentSessionState["coins_collected"];
|
||||
if (amount <= sessionCoins)
|
||||
{
|
||||
CurrentSessionState["coins_collected"] = sessionCoins - amount;
|
||||
}
|
||||
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)
|
||||
{
|
||||
return ((Array)PlayerState["unlocked_skills"]).Contains(skill)
|
||||
|| ((Array)CurrentSessionState["skills_unlocked"]).Contains(skill);
|
||||
}
|
||||
|
||||
public void UnlockSkill(SkillData skill)
|
||||
{
|
||||
if (!IsSkillUnlocked(skill))
|
||||
((Array)PlayerState["unlocked_skills"]).Add(skill);
|
||||
}
|
||||
|
||||
public void RemoveSkill(string skillName)
|
||||
{
|
||||
var arr = (Array)PlayerState["unlocked_skills"];
|
||||
foreach (SkillData s in arr)
|
||||
{
|
||||
if (s.Name != skillName) continue;
|
||||
|
||||
arr.Remove(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void UnlockSkills(Array<SkillData> skills)
|
||||
{
|
||||
foreach (var s in skills)
|
||||
UnlockSkill(s);
|
||||
}
|
||||
|
||||
public void ResetPlayerState()
|
||||
{
|
||||
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>() }
|
||||
};
|
||||
}
|
||||
|
||||
public void UnlockLevel(int levelIndex)
|
||||
{
|
||||
var unlocked = (Array)PlayerState["unlocked_levels"];
|
||||
if (!unlocked.Contains(levelIndex)) unlocked.Add(levelIndex);
|
||||
}
|
||||
|
||||
public void TryToGoToNextLevel()
|
||||
{
|
||||
var next = (int)PlayerState["current_level"] + 1;
|
||||
var unlocked = (Array)PlayerState["unlocked_levels"];
|
||||
if (next < LevelScenes.Count && unlocked.Contains(next))
|
||||
{
|
||||
PlayerState["current_level"] = next;
|
||||
GetTree().ChangeSceneToPacked(LevelScenes[next]);
|
||||
}
|
||||
}
|
||||
|
||||
public void MarkLevelComplete(int levelIndex)
|
||||
{
|
||||
UnlockLevel(levelIndex + 1);
|
||||
var completed = (Array)PlayerState["completed_levels"];
|
||||
if (!completed.Contains(levelIndex)) completed.Add(levelIndex);
|
||||
}
|
||||
|
||||
public void ResetCurrentSessionState()
|
||||
{
|
||||
CurrentSessionState = new Dictionary
|
||||
{
|
||||
{ "coins_collected", 0 },
|
||||
{ "skills_unlocked", new Array<SkillData>() }
|
||||
};
|
||||
}
|
||||
|
||||
public void RestartGame()
|
||||
{
|
||||
ResetPlayerState();
|
||||
ResetCurrentSessionState();
|
||||
GetTree().ChangeSceneToPacked(LevelScenes[0]);
|
||||
GetNode<SaveSystem>("/root/SaveSystem").SaveGame();
|
||||
}
|
||||
|
||||
public void QuitGame() => GetTree().Quit();
|
||||
|
||||
public void PauseGame() => Engine.TimeScale = 0;
|
||||
public void ResumeGame() => Engine.TimeScale = 1;
|
||||
|
||||
public void StartNewGame()
|
||||
{
|
||||
ResetPlayerState();
|
||||
ResetCurrentSessionState();
|
||||
GetTree().ChangeSceneToPacked(LevelScenes[0]);
|
||||
GetNode<SaveSystem>("/root/SaveSystem").SaveGame();
|
||||
}
|
||||
|
||||
public void ContinueGame()
|
||||
{
|
||||
var save = GetNode<SaveSystem>("/root/SaveSystem");
|
||||
if (!save.LoadGame())
|
||||
{
|
||||
GD.PrintErr("Failed to load game. Starting a new game instead.");
|
||||
StartNewGame();
|
||||
return;
|
||||
}
|
||||
|
||||
var idx = (int)PlayerState["current_level"];
|
||||
if (idx < LevelScenes.Count)
|
||||
GetTree().ChangeSceneToPacked(LevelScenes[idx]);
|
||||
else
|
||||
GD.PrintErr("No levels unlocked to continue.");
|
||||
}
|
||||
|
||||
public void OnLevelComplete()
|
||||
{
|
||||
var levelIndex = (int)PlayerState["current_level"];
|
||||
MarkLevelComplete(levelIndex);
|
||||
AddCoins((int)CurrentSessionState["coins_collected"]);
|
||||
foreach (var s in (Array)CurrentSessionState["skills_unlocked"])
|
||||
UnlockSkill((SkillData)s);
|
||||
|
||||
ResetCurrentSessionState();
|
||||
TryToGoToNextLevel();
|
||||
GetNode<SaveSystem>("/root/SaveSystem").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;
|
||||
}
|
||||
|
||||
public PlayerController GetPlayer()
|
||||
{
|
||||
if (Player != null) return Player;
|
||||
|
||||
foreach (var node in _sceneNodes)
|
||||
{
|
||||
if (node is not PlayerController player) continue;
|
||||
|
||||
Player = player;
|
||||
return Player;
|
||||
}
|
||||
|
||||
GD.PrintErr("PlayerController not found in the scene tree.");
|
||||
return null;
|
||||
}
|
||||
}
|
1
Autoloads/GameManager.cs.uid
Normal file
1
Autoloads/GameManager.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c6eoi3ymefc0x
|
61
Autoloads/SaveSystem.cs
Normal file
61
Autoloads/SaveSystem.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using Mr.BrickAdventures.scripts.Resources;
|
||||
|
||||
namespace Mr.BrickAdventures.Autoloads;
|
||||
|
||||
public partial class SaveSystem : Node
|
||||
{
|
||||
[Export] public string SavePath { get; set; } = "user://savegame.save";
|
||||
[Export] public int Version { get; set; } = 1;
|
||||
|
||||
private GameManager _gameManager;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_gameManager = GetNode<GameManager>("/root/GameManager");
|
||||
}
|
||||
|
||||
public void SaveGame()
|
||||
{
|
||||
var saveData = new Dictionary
|
||||
{
|
||||
{ "player_state", _gameManager.PlayerState},
|
||||
{ "version", Version}
|
||||
};
|
||||
|
||||
using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Write);
|
||||
file.StoreVar(saveData);
|
||||
GD.Print("Game state saved to: ", SavePath);
|
||||
}
|
||||
|
||||
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"]}");
|
||||
return false;
|
||||
}
|
||||
|
||||
GD.Print("Game state loaded from: ", SavePath);
|
||||
GD.Print("Player state: ", saveDataObj["player_state"]);
|
||||
_gameManager.PlayerState = (Dictionary)saveDataObj["player_state"];
|
||||
|
||||
var skills = new Array<SkillData>();
|
||||
foreach (var skill in (Array<SkillData>)_gameManager.PlayerState["unlocked_skills"])
|
||||
{
|
||||
skills.Add(skill);
|
||||
}
|
||||
|
||||
_gameManager.UnlockSkills(skills);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CheckSaveExists() => FileAccess.FileExists(SavePath);
|
||||
}
|
1
Autoloads/SaveSystem.cs.uid
Normal file
1
Autoloads/SaveSystem.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bh20fqbyifidc
|
65
Autoloads/UIManager.cs
Normal file
65
Autoloads/UIManager.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace Mr.BrickAdventures.Autoloads;
|
||||
|
||||
public partial class UIManager : Node
|
||||
{
|
||||
[Export] public Array<Control> UiStack { get; set; } = new();
|
||||
|
||||
[Signal] public delegate void ScreenPushedEventHandler(Control screen);
|
||||
[Signal] public delegate void ScreenPoppedEventHandler(Control screen);
|
||||
|
||||
public void PushScreen(Control screen)
|
||||
{
|
||||
if (screen == null)
|
||||
{
|
||||
GD.PushError($"Cannot push a null screen.");
|
||||
return;
|
||||
}
|
||||
|
||||
UiStack.Add(screen);
|
||||
screen.Show();
|
||||
screen.SetProcessInput(true);
|
||||
screen.SetFocusMode(Control.FocusModeEnum.All);
|
||||
screen.GrabFocus();
|
||||
EmitSignalScreenPushed(screen);
|
||||
}
|
||||
|
||||
public void PopScreen()
|
||||
{
|
||||
if (UiStack.Count == 0)
|
||||
{
|
||||
GD.PushError($"Cannot pop screen from an empty stack.");
|
||||
return;
|
||||
}
|
||||
|
||||
var top = (Control)UiStack[^1];
|
||||
UiStack.RemoveAt(UiStack.Count - 1);
|
||||
top.Hide();
|
||||
top.SetProcessInput(false);
|
||||
EmitSignalScreenPopped(top);
|
||||
top.AcceptEvent();
|
||||
|
||||
if (UiStack.Count > 0) ((Control)UiStack[^1]).GrabFocus();
|
||||
}
|
||||
|
||||
public Control TopScreen() => UiStack.Count > 0 ? (Control)UiStack[^1] : null;
|
||||
|
||||
public bool IsScreenOnTop(Control screen) => UiStack.Count > 0 && (Control)UiStack[^1] == screen;
|
||||
|
||||
public bool IsVisibleOnStack(Control screen) => UiStack.Contains(screen) && screen.Visible;
|
||||
|
||||
public void CloseAll()
|
||||
{
|
||||
while (UiStack.Count > 0)
|
||||
PopScreen();
|
||||
}
|
||||
|
||||
public static void HideAndDisable(Control screen)
|
||||
{
|
||||
screen.Hide();
|
||||
screen.SetProcessInput(false);
|
||||
}
|
||||
|
||||
}
|
1
Autoloads/UIManager.cs.uid
Normal file
1
Autoloads/UIManager.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c3ldmnrwperr4
|
13
Mr. Brick Adventures.csproj
Normal file
13
Mr. Brick Adventures.csproj
Normal file
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Godot.NET.Sdk/4.4.1">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
<RootNamespace>Mr.BrickAdventures</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Chickensoft.AutoInject" Version="2.8.21" />
|
||||
<PackageReference Include="Chickensoft.GodotNodeInterfaces" Version="2.4.31" />
|
||||
<PackageReference Include="Chickensoft.Introspection" Version="3.0.2" />
|
||||
<PackageReference Include="Chickensoft.Introspection.Generator" Version="3.0.2" PrivateAssets="all" OutputItemType="analyzer" />
|
||||
</ItemGroup>
|
||||
</Project>
|
19
Mr. Brick Adventures.sln
Normal file
19
Mr. Brick Adventures.sln
Normal file
@@ -0,0 +1,19 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 2012
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mr. Brick Adventures", "Mr. Brick Adventures.csproj", "{A1D482B9-207B-4D6C-A0A0-D9E6D1AE2356}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
ExportDebug|Any CPU = ExportDebug|Any CPU
|
||||
ExportRelease|Any CPU = ExportRelease|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A1D482B9-207B-4D6C-A0A0-D9E6D1AE2356}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A1D482B9-207B-4D6C-A0A0-D9E6D1AE2356}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A1D482B9-207B-4D6C-A0A0-D9E6D1AE2356}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU
|
||||
{A1D482B9-207B-4D6C-A0A0-D9E6D1AE2356}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU
|
||||
{A1D482B9-207B-4D6C-A0A0-D9E6D1AE2356}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU
|
||||
{A1D482B9-207B-4D6C-A0A0-D9E6D1AE2356}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
6
Mr. Brick Adventures.sln.DotSettings.user
Normal file
6
Mr. Brick Adventures.sln.DotSettings.user
Normal file
@@ -0,0 +1,6 @@
|
||||
<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_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_003ACollisionShape2D_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F2ca9b7334678f5c97c7c2a9fbe4837be71cae11b6a30408dd4791b18f997e4a_003FCollisionShape2D_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_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_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>
|
18
README.md
18
README.md
@@ -1,5 +1,12 @@
|
||||
# Przygody Pana Cegły
|
||||
|
||||
[](https://store.steampowered.com/app/3575090/Mr_Brick_Adventures/)
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
*"The world’s only brick-throwing dad simulator (probably)."*
|
||||
|
||||
---
|
||||
@@ -41,6 +48,15 @@ Run, jump, and hurl bricks with pinpoint accuracy as you navigate dangerous worl
|
||||
|
||||
---
|
||||
|
||||
## 📊 Project Stats
|
||||
📦 **Lines of Code:**
|
||||

|
||||
|
||||
📈 **Repo Activity:**
|
||||

|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
Check the [LICENSE](./LICENSE) for the legal stuff.
|
||||
@@ -53,4 +69,4 @@ Not open to code contributions right now but you *can* help by:
|
||||
|
||||
* Reporting bugs
|
||||
* Suggesting evil new trap ideas
|
||||
* Spreading the word so Mr. Brick can find his kids faster
|
||||
* Spreading the word so Mr. Brick can find his kids faster
|
||||
|
10
app/IGameScenes.cs
Normal file
10
app/IGameScenes.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Godot;
|
||||
|
||||
namespace Mr.BrickAdventures.app;
|
||||
|
||||
public interface IGameScenes
|
||||
{
|
||||
void Load(PackedScene scene);
|
||||
void Restart();
|
||||
void ReturnToMain(PackedScene mainMenu);
|
||||
}
|
1
app/IGameScenes.cs.uid
Normal file
1
app/IGameScenes.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b7d1ospa5p4nx
|
11
app/ILevelCatalog.cs
Normal file
11
app/ILevelCatalog.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Godot;
|
||||
|
||||
namespace Mr.BrickAdventures.app;
|
||||
|
||||
public interface ILevelCatalog
|
||||
{
|
||||
int Count { get; }
|
||||
PackedScene? Get(int index);
|
||||
PackedScene First { get; }
|
||||
PackedScene MainMenu { get; }
|
||||
}
|
1
app/ILevelCatalog.cs.uid
Normal file
1
app/ILevelCatalog.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b2sxttgvk6nuq
|
@@ -51,4 +51,4 @@ func close_all() -> void:
|
||||
|
||||
func hide_and_disable(screen: Control) -> void:
|
||||
screen.hide()
|
||||
screen.set_process_input(false)
|
||||
screen.set_process_input(false)
|
||||
|
70
common/AppRoot.cs
Normal file
70
common/AppRoot.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using Mr.BrickAdventures.app;
|
||||
using Mr.BrickAdventures.data;
|
||||
using Mr.BrickAdventures.game.repositories;
|
||||
using Mr.BrickAdventures.game.services;
|
||||
|
||||
namespace Mr.BrickAdventures.common;
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
public partial class AppRoot : Node2D,
|
||||
IProvide<PlayerRepository>,
|
||||
IProvide<LevelRepository>,
|
||||
IProvide<SaveClient>,
|
||||
IProvide<SaveService>,
|
||||
IProvide<LevelService>,
|
||||
IProvide<IGameScenes>,
|
||||
IProvide<ILevelCatalog>,
|
||||
IGameScenes
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
|
||||
[Export] private Array<PackedScene> _levels = [];
|
||||
[Export] private PackedScene _mainMenu = null!;
|
||||
|
||||
private readonly SaveClient _save = new("user://savegame.save", version: 2);
|
||||
private readonly PlayerRepository _players = new();
|
||||
private readonly LevelRepository _levelsRepo = new();
|
||||
private SaveService _saveService = null!;
|
||||
private LevelService _levelService = null!;
|
||||
private ILevelCatalog _catalog = null!;
|
||||
|
||||
PlayerRepository IProvide<PlayerRepository>.Value() => _players;
|
||||
LevelRepository IProvide<LevelRepository>.Value() => _levelsRepo;
|
||||
SaveClient IProvide<SaveClient>.Value() => _save;
|
||||
SaveService IProvide<SaveService>.Value() => _saveService;
|
||||
LevelService IProvide<LevelService>.Value() => _levelService;
|
||||
ILevelCatalog IProvide<ILevelCatalog>.Value() => _catalog;
|
||||
IGameScenes IProvide<IGameScenes>.Value() => this;
|
||||
|
||||
|
||||
public void OnReady()
|
||||
{
|
||||
_saveService = new SaveService(_players, _levelsRepo, _save);
|
||||
_levelService = new LevelService(_levelsRepo);
|
||||
|
||||
_catalog = new ExportedLevelCatalog(_levels, _mainMenu);
|
||||
|
||||
_saveService.TryLoad();
|
||||
this.Provide();
|
||||
}
|
||||
|
||||
public void Load(PackedScene scene) => GetTree().ChangeSceneToPacked(scene);
|
||||
public void Restart() => GetTree().ReloadCurrentScene();
|
||||
public void ReturnToMain(PackedScene mainMenu) => GetTree().ChangeSceneToPacked(mainMenu);
|
||||
|
||||
private sealed class ExportedLevelCatalog : ILevelCatalog
|
||||
{
|
||||
private readonly Array<PackedScene> _levels;
|
||||
public PackedScene MainMenu { get; }
|
||||
public ExportedLevelCatalog(Array<PackedScene> levels, PackedScene mainMenu) {
|
||||
_levels = levels; MainMenu = mainMenu;
|
||||
}
|
||||
public int Count => _levels.Count;
|
||||
public PackedScene? Get(int index) => (index >= 0 && index < _levels.Count) ? _levels[index] : null;
|
||||
public PackedScene First => _levels.Count > 0 ? _levels[0] : MainMenu;
|
||||
}
|
||||
}
|
1
common/AppRoot.cs.uid
Normal file
1
common/AppRoot.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dg2l7cw6da4vb
|
6
data/ConfigClient.cs
Normal file
6
data/ConfigClient.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Mr.BrickAdventures.data;
|
||||
|
||||
public class ConfigClient
|
||||
{
|
||||
|
||||
}
|
1
data/ConfigClient.cs.uid
Normal file
1
data/ConfigClient.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://d3s774lnoljcu
|
103
data/SaveClient.cs
Normal file
103
data/SaveClient.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using Mr.BrickAdventures.game.repositories;
|
||||
|
||||
namespace Mr.BrickAdventures.data;
|
||||
|
||||
public sealed class SaveClient
|
||||
{
|
||||
private readonly string _path;
|
||||
private readonly int _version;
|
||||
|
||||
public SaveClient(string path, int version) { _path = path; _version = version; }
|
||||
|
||||
public bool Exists() => FileAccess.FileExists(_path);
|
||||
|
||||
public bool TryLoad(out PlayerState player, out LevelState level) {
|
||||
player = null!; level = null!;
|
||||
if (!Exists()) return false;
|
||||
|
||||
using var f = FileAccess.Open(_path, FileAccess.ModeFlags.Read);
|
||||
var dict = (Dictionary)f.GetVar();
|
||||
|
||||
if ((int)dict.GetValueOrDefault("version", -1) != _version) return false;
|
||||
|
||||
player = ToPlayer((Dictionary)dict["player"]);
|
||||
level = ToLevel((Dictionary)dict["level"]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Strict load: requires version + player_state + level_state.
|
||||
// If anything is off, delete the file and act as "no save".
|
||||
public bool TryLoadStrict(out PlayerState player, out LevelState level) {
|
||||
player = default!;
|
||||
level = default!;
|
||||
|
||||
if (!Exists()) return false;
|
||||
|
||||
try {
|
||||
using var f = FileAccess.Open(_path, FileAccess.ModeFlags.Read);
|
||||
if (f == null) { Delete(); return false; }
|
||||
|
||||
var dictionary = (Dictionary)f.GetVar();
|
||||
|
||||
|
||||
if (!dictionary.TryGetValue("version", out var v) || (int)v != _version) { Delete(); return false; }
|
||||
|
||||
if (!dictionary.TryGetValue("player_state", out var pObj) || (Dictionary)pObj is not { } p) { Delete(); return false; }
|
||||
if (!dictionary.TryGetValue("level_state", out var lObj) || (Dictionary)lObj is not { } l) { Delete(); return false; }
|
||||
|
||||
player = ToPlayer(p);
|
||||
level = ToLevel(l);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e) {
|
||||
GD.PushWarning($"SaveClient: load failed — deleting bad save. {e.GetType().Name}: {e.Message}");
|
||||
Delete();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Save(PlayerState player, LevelState level) {
|
||||
using var f = FileAccess.Open(_path, FileAccess.ModeFlags.Write);
|
||||
var dict = new Dictionary {
|
||||
{ "version", _version },
|
||||
{ "player", FromPlayer(player) },
|
||||
{ "level", FromLevel(level) }
|
||||
};
|
||||
f.StoreVar(dict);
|
||||
}
|
||||
|
||||
public void Delete() {
|
||||
if (!Exists()) return;
|
||||
var abs = ProjectSettings.GlobalizePath(_path);
|
||||
var ok = DirAccess.RemoveAbsolute(abs);
|
||||
if (ok != Error.Ok) GD.PushWarning($"SaveClient: failed to delete {_path}: {ok}");
|
||||
}
|
||||
|
||||
private static Dictionary FromPlayer(PlayerState s) => new() { { "coins", s.Coins }, { "lives", s.Lives } };
|
||||
private static PlayerState ToPlayer(Dictionary d) => new() {
|
||||
Coins = d.TryGetValue("coins", out var c) ? (int)c : 0,
|
||||
Lives = d.TryGetValue("lives", out var l) ? (int)l : 3
|
||||
};
|
||||
|
||||
private static Dictionary FromLevel(LevelState s) => new() {
|
||||
{ "current", s.Current },
|
||||
{ "unlocked", new Array<int>(s.Unlocked) },
|
||||
{ "completed", new Array<int>(s.Completed) },
|
||||
};
|
||||
|
||||
private static LevelState ToLevel(Dictionary d) => new() {
|
||||
Current = d.TryGetValue("current", out var cur) ? (int)cur : 0,
|
||||
Unlocked = d.TryGetValue("unlocked", out var ul) && (Array<int>)ul is { } a1 ? a1.ToArray() : [0],
|
||||
Completed = d.TryGetValue("completed", out var cl) && (Array<int>)cl is { } a2 ? a2.ToArray() : [],
|
||||
};
|
||||
}
|
||||
|
||||
public record SaveSnapshot {
|
||||
public required PlayerState PlayerState { get; init; }
|
||||
public required LevelState LevelState { get; init; }
|
||||
}
|
1
data/SaveClient.cs.uid
Normal file
1
data/SaveClient.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bsf45t7m2wa07
|
40
features/level/ExitDoorComponent.cs
Normal file
40
features/level/ExitDoorComponent.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
using Mr.BrickAdventures.app;
|
||||
using Mr.BrickAdventures.game.services;
|
||||
|
||||
namespace Mr.BrickAdventures.features.level;
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
public partial class ExitDoorComponent : Area2D
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
|
||||
[Export] public CollisionShape2D UnlockIndicator { get; set; } = null!;
|
||||
[Export] public bool Unlocked { get; set; } = false;
|
||||
|
||||
[Dependency] public LevelService Levels => this.DependOn<LevelService>();
|
||||
[Dependency] public ILevelCatalog Catalog => this.DependOn<ILevelCatalog>();
|
||||
[Dependency] public IGameScenes Scenes => this.DependOn<IGameScenes>();
|
||||
|
||||
public void OnReady() {
|
||||
BodyEntered += OnBodyEntered;
|
||||
UpdateVisuals();
|
||||
}
|
||||
|
||||
private void OnBodyEntered(Node body) {
|
||||
if (!Unlocked) return;
|
||||
if (body is not CharacterBody2D) return;
|
||||
|
||||
var nextIdx = Levels.CompleteAndAdvance();
|
||||
var next = Catalog.Get(nextIdx);
|
||||
if (next != null) Scenes.Load(next); else Scenes.ReturnToMain(Catalog.MainMenu);
|
||||
}
|
||||
|
||||
private void UpdateVisuals() {
|
||||
if (UnlockIndicator != null) UnlockIndicator.Disabled = !Unlocked;
|
||||
}
|
||||
|
||||
public void SetUnlocked(bool value) { Unlocked = value; UpdateVisuals(); }
|
||||
}
|
1
features/level/ExitDoorComponent.cs.uid
Normal file
1
features/level/ExitDoorComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://ddh0jm0heqtc3
|
34
features/ui/hud/Hud.cs
Normal file
34
features/ui/hud/Hud.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
using Mr.BrickAdventures.game.repositories;
|
||||
using Mr.BrickAdventures.scripts.components;
|
||||
|
||||
namespace Mr.BrickAdventures.features.ui.hud;
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
public partial class Hud : Node
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
|
||||
[Export] public HealthComponent Health { get; set; } = null!;
|
||||
[Node] public Label CoinsLabel { get; set; } = null!;
|
||||
[Node] public ProgressBar HealthBar { get; set; } = null!;
|
||||
[Node] public Label LivesLabel { get; set; } = null!;
|
||||
|
||||
[Dependency] public PlayerRepository Player => this.DependOn<PlayerRepository>();
|
||||
|
||||
public void OnResolved() {
|
||||
CoinsLabel.Text = $"{Tr("COINS_LABEL")}: {Player.Coins}";
|
||||
LivesLabel.Text = $"{Tr("LIVES_LABEL")}: {Player.Lives}";
|
||||
|
||||
Player.CoinsChanged += c => CoinsLabel.Text = $"{Tr("COINS_LABEL")}: {c}";
|
||||
Player.LivesChanged += l => LivesLabel.Text = $"{Tr("LIVES_LABEL")}: {l}";
|
||||
|
||||
if (Health != null) {
|
||||
HealthBar.MaxValue = Health.MaxHealth;
|
||||
HealthBar.Value = Health.Health;
|
||||
Health.HealthChanged += (_, total) => { HealthBar.Value = total; };
|
||||
}
|
||||
}
|
||||
}
|
1
features/ui/hud/Hud.cs.uid
Normal file
1
features/ui/hud/Hud.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c1uwe5e1cfdxl
|
@@ -1,7 +1,7 @@
|
||||
[gd_scene load_steps=8 format=3 uid="uid://byxf45ukq82pe"]
|
||||
|
||||
[ext_resource type="LabelSettings" uid="uid://rvn5ivivfvv6" path="res://resources/ui/hud_label_settings.tres" id="1_4dsh5"]
|
||||
[ext_resource type="Script" uid="uid://c3pde84b3kdco" path="res://scripts/ui/hud.gd" id="1_ueofj"]
|
||||
[ext_resource type="Script" uid="uid://c1uwe5e1cfdxl" path="res://features/ui/hud/Hud.cs" id="1_m4pq7"]
|
||||
[ext_resource type="FontFile" uid="uid://xm0vbusjr7b7" path="res://fonts/PressStart2P-Regular.ttf" id="1_ygmwt"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_mmcdi"]
|
||||
@@ -16,7 +16,7 @@ bg_color = Color(0.47451, 0.47451, 0.47451, 1)
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_22dp1"]
|
||||
bg_color = Color(0.858824, 0.254902, 0.380392, 1)
|
||||
|
||||
[node name="HUD" type="Control" node_paths=PackedStringArray("coins_label", "health_progressbar", "lives_label")]
|
||||
[node name="HUD" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
@@ -25,10 +25,7 @@ grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 4
|
||||
script = ExtResource("1_ueofj")
|
||||
coins_label = NodePath("PanelContainer/MarginContainer/HBoxContainer/Coins label")
|
||||
health_progressbar = NodePath("PanelContainer/MarginContainer/HBoxContainer/ProgressBar")
|
||||
lives_label = NodePath("PanelContainer/MarginContainer/HBoxContainer/Lives")
|
||||
script = ExtResource("1_m4pq7")
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="."]
|
||||
layout_mode = 1
|
||||
@@ -57,7 +54,8 @@ text = "HEALTH_LABEL"
|
||||
label_settings = ExtResource("1_4dsh5")
|
||||
uppercase = true
|
||||
|
||||
[node name="ProgressBar" type="ProgressBar" parent="PanelContainer/MarginContainer/HBoxContainer"]
|
||||
[node name="HealthBar" type="ProgressBar" parent="PanelContainer/MarginContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
@@ -69,7 +67,8 @@ step = 0.1
|
||||
value = 60.0
|
||||
show_percentage = false
|
||||
|
||||
[node name="Lives" type="Label" parent="PanelContainer/MarginContainer/HBoxContainer"]
|
||||
[node name="LivesLabel" type="Label" parent="PanelContainer/MarginContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "LIVES_LABEL"
|
||||
@@ -77,7 +76,8 @@ label_settings = ExtResource("1_4dsh5")
|
||||
horizontal_alignment = 1
|
||||
uppercase = true
|
||||
|
||||
[node name="Coins label" type="Label" parent="PanelContainer/MarginContainer/HBoxContainer"]
|
||||
[node name="CoinsLabel" type="Label" parent="PanelContainer/MarginContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "COINS_LABEL"
|
26
features/ui/menus/GameOverScreen.cs
Normal file
26
features/ui/menus/GameOverScreen.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
using Mr.BrickAdventures.app;
|
||||
|
||||
namespace Mr.BrickAdventures.features.ui.menus;
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
public partial class GameOverScreen : Control
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
|
||||
[Export] public Button RestartButton { get; set; } = null!;
|
||||
[Export] public Button MainMenuButton { get; set; } = null!;
|
||||
[Export] public PackedScene MainMenuScene { get; set; } = null!;
|
||||
|
||||
[Dependency] public IGameScenes Scenes => this.DependOn<IGameScenes>();
|
||||
|
||||
public void OnReady() {
|
||||
Visible = false;
|
||||
RestartButton.Pressed += () => Scenes.Restart();
|
||||
MainMenuButton.Pressed += () => Scenes.ReturnToMain(MainMenuScene);
|
||||
}
|
||||
|
||||
public void ShowGameOver() => Show();
|
||||
}
|
1
features/ui/menus/GameOverScreen.cs.uid
Normal file
1
features/ui/menus/GameOverScreen.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b36jv878751ta
|
57
features/ui/menus/MainMenu.cs
Normal file
57
features/ui/menus/MainMenu.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
using Mr.BrickAdventures.app;
|
||||
using Mr.BrickAdventures.game.repositories;
|
||||
using Mr.BrickAdventures.game.services;
|
||||
|
||||
namespace Mr.BrickAdventures.features.ui.menus;
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
public partial class MainMenu : Control
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
|
||||
[Node] public Button NewGameButton { get; set; } = null!;
|
||||
[Node] public Button ContinueButton { get; set; } = null!;
|
||||
[Node] public Button SettingsButton { get; set; } = null!;
|
||||
[Node] public Button CreditsButton { get; set; } = null!;
|
||||
[Node] public Button ExitButton { get; set; } = null!;
|
||||
[Node] public Label VersionLabel { get; set; } = null!;
|
||||
[Export] public Control SettingsControl { get; set; } = null!;
|
||||
[Export] public Control CreditsControl { get; set; } = null!;
|
||||
|
||||
[Dependency] public SaveService Save => this.DependOn<SaveService>();
|
||||
[Dependency] public LevelRepository Levels => this.DependOn<LevelRepository>();
|
||||
[Dependency] public IGameScenes Scenes => this.DependOn<IGameScenes>();
|
||||
[Dependency] public ILevelCatalog Catalog => this.DependOn<ILevelCatalog>();
|
||||
|
||||
public void OnReady() {
|
||||
VersionLabel.Text = $"v. {ProjectSettings.GetSetting("application/config/version")}";
|
||||
|
||||
NewGameButton.Pressed += OnNewGamePressed;
|
||||
ContinueButton.Pressed += OnContinuePressed;
|
||||
SettingsButton.Pressed += () => SettingsControl.Show();
|
||||
CreditsButton.Pressed += () => CreditsControl.Show();
|
||||
ExitButton.Pressed += () => GetTree().Quit();
|
||||
}
|
||||
|
||||
public void OnResolved()
|
||||
{
|
||||
ContinueButton.Disabled = !Save.Exists();
|
||||
(ContinueButton.Disabled ? NewGameButton : ContinueButton).GrabFocus();
|
||||
}
|
||||
|
||||
private void OnNewGamePressed() {
|
||||
Save.NewGame();
|
||||
Levels.SetCurrent(0);
|
||||
var first = Catalog.First;
|
||||
Scenes.Load(first);
|
||||
}
|
||||
|
||||
private void OnContinuePressed() {
|
||||
if (!Save.TryLoad()) { OnNewGamePressed(); return; }
|
||||
var scene = Catalog.Get(Levels.Current) ?? Catalog.First;
|
||||
Scenes.Load(scene);
|
||||
}
|
||||
}
|
1
features/ui/menus/MainMenu.cs.uid
Normal file
1
features/ui/menus/MainMenu.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://fcsg0e8s36in
|
33
features/ui/menus/PauseMenu.cs
Normal file
33
features/ui/menus/PauseMenu.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
using Mr.BrickAdventures.app;
|
||||
using Mr.BrickAdventures.game.services;
|
||||
|
||||
namespace Mr.BrickAdventures.features.ui.menus;
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
public partial class PauseMenu : Control
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
|
||||
[Export] public Button ResumeButton { get; set; } = null!;
|
||||
[Export] public Button RestartButton { get; set; } = null!;
|
||||
[Export] public Button MainMenuButton { get; set; } = null!;
|
||||
|
||||
[Dependency] public IGameScenes Scenes => this.DependOn<IGameScenes>();
|
||||
[Dependency] public ILevelCatalog Catalog => this.DependOn<ILevelCatalog>();
|
||||
[Dependency] public LevelService Levels => this.DependOn<LevelService>();
|
||||
|
||||
public void OnReady() {
|
||||
Visible = false;
|
||||
ResumeButton.Pressed += () => { GetTree().Paused = false; Hide(); };
|
||||
RestartButton.Pressed += () => { GetTree().Paused = false; Scenes.Restart(); };
|
||||
MainMenuButton.Pressed += () => { GetTree().Paused = false; Scenes.ReturnToMain(Catalog.MainMenu); };
|
||||
}
|
||||
|
||||
public void Toggle() {
|
||||
if (Visible) { GetTree().Paused = false; Hide(); }
|
||||
else { Show(); GetTree().Paused = true; }
|
||||
}
|
||||
}
|
1
features/ui/menus/PauseMenu.cs.uid
Normal file
1
features/ui/menus/PauseMenu.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bwgs02wcfnm8u
|
@@ -1,25 +1,18 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://8b6ol5sssbgo"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://hyfvthdbgjbc" path="res://scripts/ui/main_menu.gd" id="1_epxpl"]
|
||||
[ext_resource type="Script" uid="uid://fcsg0e8s36in" path="res://features/ui/menus/MainMenu.cs" id="1_q8hru"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_qv2q0"]
|
||||
bg_color = Color(0, 0, 0, 1)
|
||||
|
||||
[node name="MainMenu" type="Control" node_paths=PackedStringArray("main_menu_control", "new_game_button", "continue_button", "settings_button", "credits_button", "exit_button", "version_label")]
|
||||
[node name="MainMenu" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_epxpl")
|
||||
main_menu_control = NodePath(".")
|
||||
new_game_button = NodePath("PanelContainer/MarginContainer/VBoxContainer/NewGameButton")
|
||||
continue_button = NodePath("PanelContainer/MarginContainer/VBoxContainer/ContinueButton")
|
||||
settings_button = NodePath("PanelContainer/MarginContainer/VBoxContainer/SettingsButton")
|
||||
credits_button = NodePath("PanelContainer/MarginContainer/VBoxContainer/CreditsButton")
|
||||
exit_button = NodePath("PanelContainer/MarginContainer/VBoxContainer/QuitButton")
|
||||
version_label = NodePath("PanelContainer/MarginContainer/VBoxContainer/version")
|
||||
script = ExtResource("1_q8hru")
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="."]
|
||||
layout_mode = 1
|
||||
@@ -53,31 +46,37 @@ layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="ContinueButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "CONTINUE_BUTTON"
|
||||
flat = true
|
||||
|
||||
[node name="NewGameButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "NEW_GAME_BUTTON"
|
||||
flat = true
|
||||
|
||||
[node name="SettingsButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "SETTINGS_BUTTON"
|
||||
flat = true
|
||||
|
||||
[node name="CreditsButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "CREDITS_BUTTON"
|
||||
flat = true
|
||||
|
||||
[node name="QuitButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer"]
|
||||
[node name="ExitButton" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "QUIT_BUTTON"
|
||||
flat = true
|
||||
|
||||
[node name="version" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer"]
|
||||
[node name="VersionLabel" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 8
|
6
features/ui/settings/AudioSettings.cs
Normal file
6
features/ui/settings/AudioSettings.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Mr.BrickAdventures.features.ui.settings;
|
||||
|
||||
public class AudioSettings
|
||||
{
|
||||
|
||||
}
|
1
features/ui/settings/AudioSettings.cs.uid
Normal file
1
features/ui/settings/AudioSettings.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bnj36dhskiem
|
34
game/repositories/LevelRepository.cs
Normal file
34
game/repositories/LevelRepository.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Mr.BrickAdventures.game.repositories;
|
||||
|
||||
public sealed class LevelRepository
|
||||
{
|
||||
public int Current { get; private set; } = 0;
|
||||
public HashSet<int> Unlocked { get; } = new() { 0 };
|
||||
public HashSet<int> Completed { get; } = new();
|
||||
|
||||
public event Action<int>? CurrentChanged;
|
||||
|
||||
public void SetCurrent(int idx) { Current = idx; CurrentChanged?.Invoke(Current); }
|
||||
public void Unlock(int idx) => Unlocked.Add(idx);
|
||||
public void Complete(int idx) { Completed.Add(idx); Unlock(idx + 1); }
|
||||
|
||||
public LevelState Export() => new() {
|
||||
Current = Current, Unlocked = [..Unlocked], Completed = [..Completed]
|
||||
};
|
||||
public void Load(LevelState s) {
|
||||
Current = s.Current;
|
||||
Unlocked.Clear(); foreach (var i in s.Unlocked) Unlocked.Add(i);
|
||||
Completed.Clear(); foreach (var i in s.Completed) Completed.Add(i);
|
||||
CurrentChanged?.Invoke(Current);
|
||||
}
|
||||
}
|
||||
|
||||
public record LevelState {
|
||||
public int Current;
|
||||
public int[] Unlocked = [];
|
||||
public int[] Completed = [];
|
||||
}
|
1
game/repositories/LevelRepository.cs.uid
Normal file
1
game/repositories/LevelRepository.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://d0604lmwpadt5
|
25
game/repositories/PlayerRepository.cs
Normal file
25
game/repositories/PlayerRepository.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace Mr.BrickAdventures.game.repositories;
|
||||
|
||||
public sealed class PlayerRepository
|
||||
{
|
||||
public int Coins { get; private set; } = 0;
|
||||
public int Lives { get; private set; } = 3;
|
||||
|
||||
public event Action<int>? CoinsChanged;
|
||||
public event Action<int>? LivesChanged;
|
||||
|
||||
public void SetCoins(int value) { Coins = Math.Max(0, value); CoinsChanged?.Invoke(Coins); }
|
||||
public void AddCoins(int amount) { SetCoins(Coins + amount); }
|
||||
public void RemoveCoins(int amount){ SetCoins(Coins - amount); }
|
||||
|
||||
public void SetLives(int value) { Lives = value; LivesChanged?.Invoke(Lives); }
|
||||
public void AddLives(int amount) { SetLives(Lives + amount); }
|
||||
public void RemoveLives(int amount){ SetLives(Lives - amount); }
|
||||
|
||||
public PlayerState Export() => new() { Coins = Coins, Lives = Lives };
|
||||
public void Load(PlayerState s) { SetCoins(s.Coins); SetLives(s.Lives); }
|
||||
}
|
||||
|
||||
public record PlayerState { public int Coins; public int Lives; }
|
1
game/repositories/PlayerRepository.cs.uid
Normal file
1
game/repositories/PlayerRepository.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://d1dijkt574x4b
|
6
game/repositories/SessionRepository.cs
Normal file
6
game/repositories/SessionRepository.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Mr.BrickAdventures.game.repositories;
|
||||
|
||||
public sealed class SessionRepository
|
||||
{
|
||||
|
||||
}
|
1
game/repositories/SessionRepository.cs.uid
Normal file
1
game/repositories/SessionRepository.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b550hcqugygv0
|
6
game/repositories/SettingsRepository.cs
Normal file
6
game/repositories/SettingsRepository.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Mr.BrickAdventures.game.repositories;
|
||||
|
||||
public class SettingsRepository
|
||||
{
|
||||
|
||||
}
|
1
game/repositories/SettingsRepository.cs.uid
Normal file
1
game/repositories/SettingsRepository.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bk11iduo7bg2h
|
6
game/repositories/SkillsRepository.cs
Normal file
6
game/repositories/SkillsRepository.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Mr.BrickAdventures.game.repositories;
|
||||
|
||||
public class SkillsRepository
|
||||
{
|
||||
|
||||
}
|
1
game/repositories/SkillsRepository.cs.uid
Normal file
1
game/repositories/SkillsRepository.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cymx7mtmdblun
|
21
game/services/LevelService.cs
Normal file
21
game/services/LevelService.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Mr.BrickAdventures.game.repositories;
|
||||
|
||||
namespace Mr.BrickAdventures.game.services;
|
||||
|
||||
public sealed class LevelService
|
||||
{
|
||||
private readonly LevelRepository _levels;
|
||||
public LevelService(LevelRepository levels) => _levels = levels;
|
||||
|
||||
public int CompleteAndAdvance() {
|
||||
var cur = _levels.Current;
|
||||
_levels.Complete(cur);
|
||||
var next = cur + 1;
|
||||
_levels.SetCurrent(next);
|
||||
return next;
|
||||
}
|
||||
|
||||
public void StartNew() {
|
||||
_levels.Load(new LevelState { Current = 0, Unlocked = new [] { 0 }, Completed = [] });
|
||||
}
|
||||
}
|
1
game/services/LevelService.cs.uid
Normal file
1
game/services/LevelService.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b2tsi3cjnh5xq
|
32
game/services/SaveService.cs
Normal file
32
game/services/SaveService.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using Mr.BrickAdventures.data;
|
||||
using Mr.BrickAdventures.game.repositories;
|
||||
|
||||
namespace Mr.BrickAdventures.game.services;
|
||||
|
||||
public sealed class SaveService
|
||||
{
|
||||
private readonly PlayerRepository _players;
|
||||
private readonly LevelRepository _levels;
|
||||
private readonly SaveClient _save;
|
||||
|
||||
public SaveService(PlayerRepository players, LevelRepository levels, SaveClient save) {
|
||||
_players = players; _levels = levels; _save = save;
|
||||
}
|
||||
|
||||
|
||||
public bool TryLoad() {
|
||||
if (!_save.TryLoad(out var p, out var l)) return false;
|
||||
_players.Load(p);
|
||||
_levels.Load(l);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Save() => _save.Save(_players.Export(), _levels.Export());
|
||||
public bool Exists() => _save.Exists();
|
||||
|
||||
public void NewGame() {
|
||||
_players.Load(new PlayerState { Coins = 0, Lives = 3 });
|
||||
_levels.Load(new LevelState { Current = 0, Unlocked = [0], Completed = [] });
|
||||
Save();
|
||||
}
|
||||
}
|
1
game/services/SaveService.cs.uid
Normal file
1
game/services/SaveService.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dxqkxlkkivxog
|
6
game/services/SkillService.cs
Normal file
6
game/services/SkillService.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Mr.BrickAdventures.game.services;
|
||||
|
||||
public sealed class SkillService
|
||||
{
|
||||
|
||||
}
|
1
game/services/SkillService.cs.uid
Normal file
1
game/services/SkillService.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dim21hs12wdi5
|
@@ -32,7 +32,7 @@
|
||||
[ext_resource type="PackedScene" uid="uid://bg76mtpcmfm2j" path="res://objects/ui/charging_bar_layer.tscn" id="28_3f5nm"]
|
||||
[ext_resource type="PackedScene" uid="uid://b12tppjkkqpt4" path="res://objects/fxs/hit_particles.tscn" id="28_jh5m0"]
|
||||
[ext_resource type="Script" uid="uid://ceq8n7yw7qxpi" path="res://scripts/components/hit_component.gd" id="29_jh5m0"]
|
||||
[ext_resource type="Script" uid="uid://c1wtrgw0x77xo" path="res://scripts/components/platform_movement.gd" id="31_xoue7"]
|
||||
[ext_resource type="Script" uid="uid://cflncpa377l8l" path="res://scripts/components/platform_movement.gd" id="31_xoue7"]
|
||||
[ext_resource type="AudioStream" uid="uid://dyev46uqusimi" path="res://sfx/shoot.wav" id="32_x2b7c"]
|
||||
[ext_resource type="Script" uid="uid://d1ctdx52gskv1" path="res://scripts/components/ship_shooter.gd" id="34_gwc8i"]
|
||||
[ext_resource type="PackedScene" uid="uid://dtem8jgcyoqar" path="res://objects/entities/green_laser.tscn" id="36_oxudy"]
|
||||
|
@@ -62,9 +62,6 @@ process_material = SubResource("ParticleProcessMaterial_lgb3u")
|
||||
|
||||
[node name="UI Layer" parent="." instance=ExtResource("2_lbnsn")]
|
||||
|
||||
[node name="HUD" parent="UI Layer" index="0" node_paths=PackedStringArray("player_health")]
|
||||
player_health = NodePath("../../Brick Player/HealthComponent")
|
||||
|
||||
[node name="DeathScreen" parent="UI Layer" index="1" node_paths=PackedStringArray("nodes_to_disable")]
|
||||
current_level = ExtResource("4_c2yv5")
|
||||
nodes_to_disable = [NodePath("../../Brick Player")]
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[gd_scene load_steps=11 format=3 uid="uid://6foggu31cu14"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://byxf45ukq82pe" path="res://objects/ui/hud.tscn" id="1_tgtfe"]
|
||||
[ext_resource type="PackedScene" uid="uid://byxf45ukq82pe" path="res://features/ui/hud/hud.tscn" id="1_tgtfe"]
|
||||
[ext_resource type="PackedScene" uid="uid://dulkm3ah4tm0u" path="res://objects/ui/death_screen.tscn" id="2_ln68j"]
|
||||
[ext_resource type="Script" uid="uid://cp68km8bykymb" path="res://scripts/resources/level_resource.gd" id="3_5kt5k"]
|
||||
[ext_resource type="PackedScene" uid="uid://wmw6gaisyrvx" path="res://objects/ui/game_over_screen.tscn" id="4_11xmk"]
|
||||
@@ -34,9 +34,8 @@ visible = false
|
||||
offset_top = 32.0
|
||||
components_to_disable = [null]
|
||||
|
||||
[node name="Pause menu" parent="." node_paths=PackedStringArray("settings_menu") instance=ExtResource("6_1q4vn")]
|
||||
[node name="Pause menu" parent="." instance=ExtResource("6_1q4vn")]
|
||||
visible = false
|
||||
settings_menu = NodePath("../Settings menu")
|
||||
|
||||
[node name="Settings menu" parent="." node_paths=PackedStringArray("input_settings", "audio_settings") instance=ExtResource("7_hkjav")]
|
||||
visible = false
|
||||
|
@@ -1,12 +1,11 @@
|
||||
[gd_scene load_steps=4 format=3 uid="uid://i6mnjbjcoqe5"]
|
||||
[gd_scene load_steps=3 format=3 uid="uid://i6mnjbjcoqe5"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cugifchx6jhuk" path="res://scripts/ui/pause_menu.gd" id="1_aktha"]
|
||||
[ext_resource type="PackedScene" uid="uid://cl00e2ocomk3m" path="res://scenes/main_menu.tscn" id="2_h4pd5"]
|
||||
[ext_resource type="Script" uid="uid://bwgs02wcfnm8u" path="res://features/ui/menus/PauseMenu.cs" id="1_ljtns"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_g4ivv"]
|
||||
bg_color = Color(0, 0, 0, 1)
|
||||
|
||||
[node name="Pause menu" type="Control" node_paths=PackedStringArray("pause_menu_control", "resume_button", "quit_button", "settings_button", "exit_to_menu_button")]
|
||||
[node name="Pause menu" type="Control" node_paths=PackedStringArray("ResumeButton", "RestartButton", "MainMenuButton")]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
@@ -15,13 +14,10 @@ grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 6
|
||||
size_flags_vertical = 6
|
||||
script = ExtResource("1_aktha")
|
||||
pause_menu_control = NodePath(".")
|
||||
resume_button = NodePath("PanelContainer/MarginContainer/VBoxContainer/Resume Button")
|
||||
quit_button = NodePath("PanelContainer/MarginContainer/VBoxContainer/Quit game Button")
|
||||
settings_button = NodePath("PanelContainer/MarginContainer/VBoxContainer/Settings Button")
|
||||
exit_to_menu_button = NodePath("PanelContainer/MarginContainer/VBoxContainer/Exit to menu Button")
|
||||
exit_to_menu_scene = ExtResource("2_h4pd5")
|
||||
script = ExtResource("1_ljtns")
|
||||
ResumeButton = NodePath("PanelContainer/MarginContainer/VBoxContainer/Resume Button")
|
||||
RestartButton = NodePath("PanelContainer/MarginContainer/VBoxContainer/Settings Button")
|
||||
MainMenuButton = NodePath("PanelContainer/MarginContainer/VBoxContainer/Exit to menu Button")
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="."]
|
||||
layout_mode = 1
|
||||
|
@@ -19,7 +19,7 @@ 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", "GL Compatibility")
|
||||
config/features=PackedStringArray("4.4", "C#", "GL Compatibility")
|
||||
run/max_fps=180
|
||||
boot_splash/bg_color=Color(0, 0, 0, 1)
|
||||
boot_splash/show_image=false
|
||||
|
@@ -66,9 +66,6 @@ process_material = SubResource("ParticleProcessMaterial_lgb3u")
|
||||
|
||||
[node name="UI Layer" parent="." instance=ExtResource("3_4fsls")]
|
||||
|
||||
[node name="HUD" parent="UI Layer" index="0" node_paths=PackedStringArray("player_health")]
|
||||
player_health = NodePath("../../Brick Player/HealthComponent")
|
||||
|
||||
[node name="DeathScreen" parent="UI Layer" index="1" node_paths=PackedStringArray("nodes_to_disable")]
|
||||
current_level = ExtResource("4_onnch")
|
||||
nodes_to_disable = [NodePath("../../Brick Player")]
|
||||
|
@@ -1,27 +1,38 @@
|
||||
[gd_scene load_steps=6 format=3 uid="uid://cl00e2ocomk3m"]
|
||||
[gd_scene load_steps=12 format=3 uid="uid://cl00e2ocomk3m"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://8b6ol5sssbgo" path="res://objects/ui/main_menu.tscn" id="1_ekxnf"]
|
||||
[ext_resource type="PackedScene" uid="uid://8b6ol5sssbgo" path="res://features/ui/menus/main_menu.tscn" id="1_ekxnf"]
|
||||
[ext_resource type="Script" uid="uid://dg2l7cw6da4vb" path="res://common/AppRoot.cs" id="1_rtw2f"]
|
||||
[ext_resource type="PackedScene" uid="uid://y0ae6e7t70fj" path="res://objects/ui/settings_menu.tscn" id="2_bqqt6"]
|
||||
[ext_resource type="PackedScene" uid="uid://bwgmrcyj4mvu" path="res://objects/ui/credits.tscn" id="3_bqqt6"]
|
||||
[ext_resource type="PackedScene" uid="uid://chqb11pfoqmeb" path="res://scenes/level_village_2.tscn" id="3_lgwnu"]
|
||||
[ext_resource type="PackedScene" uid="uid://bol7g83v2accs" path="res://scenes/level_village_1.tscn" id="3_oa1go"]
|
||||
[ext_resource type="PackedScene" uid="uid://b5fx1vdfky307" path="res://objects/ui/audio_settings.tscn" id="4_8ln24"]
|
||||
[ext_resource type="PackedScene" uid="uid://h60obxmju6mo" path="res://scenes/level_village_3.tscn" id="4_flqon"]
|
||||
[ext_resource type="PackedScene" uid="uid://bhad760x3vvco" path="res://scenes/level_village_4.tscn" id="5_rcqid"]
|
||||
[ext_resource type="PackedScene" uid="uid://cvfsbiy5ggrpg" path="res://objects/ui/input_settings.tscn" id="5_rtw2f"]
|
||||
[ext_resource type="PackedScene" uid="uid://dagpmlgvr262d" path="res://scenes/level_forest_5.tscn" id="6_1ajci"]
|
||||
|
||||
[node name="Main menu" type="CanvasLayer"]
|
||||
[node name="AppRoot" type="Node2D"]
|
||||
script = ExtResource("1_rtw2f")
|
||||
_levels = Array[PackedScene]([ExtResource("3_oa1go"), ExtResource("3_lgwnu"), ExtResource("4_flqon"), ExtResource("5_rcqid"), ExtResource("6_1ajci")])
|
||||
_mainMenu = null
|
||||
|
||||
[node name="MainMenu" parent="." node_paths=PackedStringArray("settings_control", "credits_control") instance=ExtResource("1_ekxnf")]
|
||||
settings_control = NodePath("../Settings menu")
|
||||
credits_control = NodePath("../Credits")
|
||||
[node name="Main menu" type="CanvasLayer" parent="."]
|
||||
|
||||
[node name="Settings menu" parent="." node_paths=PackedStringArray("input_settings", "audio_settings") instance=ExtResource("2_bqqt6")]
|
||||
[node name="MainMenu" parent="Main menu" node_paths=PackedStringArray("SettingsControl", "CreditsControl") instance=ExtResource("1_ekxnf")]
|
||||
SettingsControl = NodePath("../Settings menu")
|
||||
CreditsControl = NodePath("../Credits")
|
||||
|
||||
[node name="Settings menu" parent="Main menu" node_paths=PackedStringArray("input_settings", "audio_settings") instance=ExtResource("2_bqqt6")]
|
||||
visible = false
|
||||
input_settings = NodePath("../Input Settings")
|
||||
audio_settings = NodePath("../Audio settings")
|
||||
|
||||
[node name="Credits" parent="." instance=ExtResource("3_bqqt6")]
|
||||
[node name="Credits" parent="Main menu" instance=ExtResource("3_bqqt6")]
|
||||
visible = false
|
||||
|
||||
[node name="Audio settings" parent="." instance=ExtResource("4_8ln24")]
|
||||
[node name="Audio settings" parent="Main menu" instance=ExtResource("4_8ln24")]
|
||||
visible = false
|
||||
|
||||
[node name="Input Settings" parent="." instance=ExtResource("5_rtw2f")]
|
||||
[node name="Input Settings" parent="Main menu" instance=ExtResource("5_rtw2f")]
|
||||
visible = false
|
||||
|
70
scripts/Resources/ChargeThrowInputResource.cs
Normal file
70
scripts/Resources/ChargeThrowInputResource.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using Godot;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.Resources;
|
||||
|
||||
public partial class ChargeThrowInputResource : ThrowInputResource
|
||||
{
|
||||
[Export] public float MinPower { get; set; } = 0.5f;
|
||||
[Export] public float MaxPower { get; set; } = 2.0f;
|
||||
[Export] public float MaxChargeTime { get; set; } = 2.0f;
|
||||
[Export] public float MinChargeDuration { get; set; } = 0.1f;
|
||||
|
||||
private bool _isCharging = false;
|
||||
private float _chargeStartTime = 0f;
|
||||
|
||||
[Signal] public delegate void ChargeStartedEventHandler();
|
||||
[Signal] public delegate void ChargeUpdatedEventHandler(float chargeRatio);
|
||||
[Signal] public delegate void ChargeStoppedEventHandler();
|
||||
|
||||
public override void ProcessInput(InputEvent @event)
|
||||
{
|
||||
if (@event.IsActionPressed("attack"))
|
||||
{
|
||||
_isCharging = true;
|
||||
_chargeStartTime = Time.GetTicksMsec() / 1000f;
|
||||
EmitSignalChargeStarted();
|
||||
}
|
||||
|
||||
if (@event.IsActionReleased("attack") && _isCharging)
|
||||
{
|
||||
var power = CalculatePower();
|
||||
_isCharging = false;
|
||||
EmitSignalThrowRequested(power);
|
||||
EmitSignalChargeStopped();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(double delta)
|
||||
{
|
||||
if (!_isCharging) return;
|
||||
|
||||
var t = Mathf.Clamp(GetChargeRatio(), MinPower, MaxPower);
|
||||
EmitSignalChargeUpdated(t);
|
||||
}
|
||||
|
||||
public override bool SupportsCharging()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private float CalculatePower()
|
||||
{
|
||||
var now = Time.GetTicksMsec() / 1000f;
|
||||
var heldTime = now - _chargeStartTime;
|
||||
if (heldTime < MinChargeDuration)
|
||||
return MinPower;
|
||||
|
||||
var t = Mathf.Clamp(heldTime / MaxChargeTime, 0f, 1f);
|
||||
return Mathf.Lerp(MinPower, MaxPower, t);
|
||||
}
|
||||
|
||||
private float GetChargeRatio()
|
||||
{
|
||||
if (!_isCharging) return MinPower;
|
||||
|
||||
var now = Time.GetTicksMsec() / 1000f;
|
||||
var heldTime = now - _chargeStartTime;
|
||||
var t = Mathf.Clamp(heldTime / MaxChargeTime, 0f, 1f);
|
||||
return Mathf.Lerp(MinPower, MaxPower, t);
|
||||
}
|
||||
}
|
1
scripts/Resources/ChargeThrowInputResource.cs.uid
Normal file
1
scripts/Resources/ChargeThrowInputResource.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dtpdh4jp51jis
|
9
scripts/Resources/CollectableResource.cs
Normal file
9
scripts/Resources/CollectableResource.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Godot;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.Resources;
|
||||
|
||||
public partial class CollectableResource : Resource
|
||||
{
|
||||
[Export] public Variant Amount { get; set; } = 0.0;
|
||||
[Export] public CollectableType Type { get; set; }
|
||||
}
|
1
scripts/Resources/CollectableResource.cs.uid
Normal file
1
scripts/Resources/CollectableResource.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://gptsgaw3agkf
|
8
scripts/Resources/CollectableType.cs
Normal file
8
scripts/Resources/CollectableType.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Mr.BrickAdventures.scripts.Resources;
|
||||
|
||||
public enum CollectableType
|
||||
{
|
||||
Coin,
|
||||
Kid,
|
||||
Health,
|
||||
}
|
1
scripts/Resources/CollectableType.cs.uid
Normal file
1
scripts/Resources/CollectableType.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://2ql8wj3vfeke
|
19
scripts/Resources/SkillData.cs
Normal file
19
scripts/Resources/SkillData.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.Resources;
|
||||
|
||||
public partial class SkillData : Resource
|
||||
{
|
||||
[Export] public string Name { get; set; } = "New Skill";
|
||||
[Export] public string Description { get; set; } = "New Skill";
|
||||
[Export] public Dictionary<string, Variant> Config { get; set; } = new();
|
||||
[Export] public int Cost { get; set; } = 0;
|
||||
[Export] public Texture2D Icon { get; set; }
|
||||
[Export] public bool IsActive { get; set; } = false;
|
||||
[Export] public int Level { get; set; } = 1;
|
||||
[Export] public int MaxLevel { get; set; } = 1;
|
||||
[Export] public SkillType Type { get; set; } = SkillType.Throw;
|
||||
[Export] public PackedScene Node { get; set; }
|
||||
}
|
1
scripts/Resources/SkillData.cs.uid
Normal file
1
scripts/Resources/SkillData.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://d4crrfmbgxnqf
|
8
scripts/Resources/SkillType.cs
Normal file
8
scripts/Resources/SkillType.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Mr.BrickAdventures.scripts.Resources;
|
||||
|
||||
public enum SkillType
|
||||
{
|
||||
Attack,
|
||||
Throw,
|
||||
Misc,
|
||||
}
|
1
scripts/Resources/SkillType.cs.uid
Normal file
1
scripts/Resources/SkillType.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://sma20qug2r0q
|
10
scripts/Resources/StatusEffectDataResource.cs
Normal file
10
scripts/Resources/StatusEffectDataResource.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Godot;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.Resources;
|
||||
|
||||
public partial class StatusEffectDataResource : Resource
|
||||
{
|
||||
[Export] public float Duration { get; set; } = 1f;
|
||||
[Export] public float DamagePerSecond { get; set; } = 0.25f;
|
||||
[Export] public StatusEffectType Type { get; set; }
|
||||
}
|
1
scripts/Resources/StatusEffectDataResource.cs.uid
Normal file
1
scripts/Resources/StatusEffectDataResource.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://pw0pu6gb21y2
|
8
scripts/Resources/StatusEffectType.cs
Normal file
8
scripts/Resources/StatusEffectType.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Mr.BrickAdventures.scripts.Resources;
|
||||
|
||||
public enum StatusEffectType
|
||||
{
|
||||
None,
|
||||
Fire,
|
||||
Ice
|
||||
}
|
1
scripts/Resources/StatusEffectType.cs.uid
Normal file
1
scripts/Resources/StatusEffectType.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b0a7k7mse3l68
|
20
scripts/Resources/TapThrowInputResource.cs
Normal file
20
scripts/Resources/TapThrowInputResource.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures.scripts.interfaces;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.Resources;
|
||||
|
||||
public partial class TapThrowInputResource : ThrowInputResource
|
||||
{
|
||||
public override void Update(double delta)
|
||||
{
|
||||
if (Input.IsActionPressed("attack"))
|
||||
{
|
||||
EmitSignalThrowRequested(1f);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool SupportsCharging()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
1
scripts/Resources/TapThrowInputResource.cs.uid
Normal file
1
scripts/Resources/TapThrowInputResource.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cx7ryqxemgs56
|
24
scripts/Resources/ThrowInputResource.cs
Normal file
24
scripts/Resources/ThrowInputResource.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures.scripts.interfaces;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.Resources;
|
||||
|
||||
public abstract partial class ThrowInputResource : Resource, IThrowInput
|
||||
{
|
||||
[Signal] public delegate void ThrowRequestedEventHandler(float powerMultiplier = 1f);
|
||||
|
||||
public virtual void ProcessInput(InputEvent @event)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual void Update(double delta)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual bool SupportsCharging()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
1
scripts/Resources/ThrowInputResource.cs.uid
Normal file
1
scripts/Resources/ThrowInputResource.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://mnn5wy5cyr4m
|
15
scripts/Screenshot.cs
Normal file
15
scripts/Screenshot.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Godot;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts;
|
||||
|
||||
public partial class Screenshot : Node
|
||||
{
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
if (!OS.IsDebugBuild() || !Input.IsActionJustPressed("screenshot")) return;
|
||||
var img = GetViewport().GetTexture().GetImage();
|
||||
var id = OS.GetUniqueId() + "_" + Time.GetDatetimeStringFromSystem();
|
||||
var path = "user://screenshots/screenshot_" + id + ".png";
|
||||
img.SavePng(path);
|
||||
}
|
||||
}
|
1
scripts/Screenshot.cs.uid
Normal file
1
scripts/Screenshot.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://chrhjch4ymfvr
|
144
scripts/SkillManager.cs
Normal file
144
scripts/SkillManager.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
using Mr.BrickAdventures.scripts.Resources;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts;
|
||||
|
||||
public partial class SkillManager : Node
|
||||
{
|
||||
private GameManager _gameManager;
|
||||
[Export] public Array<SkillData> AvailableSkills { get; set; } = [];
|
||||
|
||||
public Dictionary ActiveComponents { get; private set; } = new();
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_gameManager = GetNode<GameManager>("/root/GameManager");
|
||||
ApplyUnlockedSkills();
|
||||
}
|
||||
|
||||
public void AddSkill(SkillData skillData)
|
||||
{
|
||||
if (ActiveComponents.ContainsKey(skillData.Name))
|
||||
return;
|
||||
|
||||
if (skillData.Type == SkillType.Throw)
|
||||
{
|
||||
var unlocked = _gameManager.GetUnlockedSkills();
|
||||
foreach (var skill in unlocked)
|
||||
{
|
||||
SkillData data = null;
|
||||
foreach (var s in AvailableSkills)
|
||||
{
|
||||
if (s == (SkillData)skill)
|
||||
{
|
||||
data = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (data != null && data.Type == SkillType.Throw)
|
||||
RemoveSkill(data.Name);
|
||||
}
|
||||
}
|
||||
|
||||
var instance = skillData.Node.Instantiate();
|
||||
foreach (var key in skillData.Config.Keys)
|
||||
{
|
||||
if (instance.HasMethod("get")) // rough presence check
|
||||
{
|
||||
var value = skillData.Config[key];
|
||||
var parent = GetParent();
|
||||
|
||||
if (value.VariantType == Variant.Type.NodePath)
|
||||
{
|
||||
var np = (NodePath)value;
|
||||
if (parent.HasNode(np))
|
||||
value = parent.GetNode(np);
|
||||
else if (instance.HasNode(np))
|
||||
value = instance.GetNode(np);
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set via property if exists
|
||||
instance.Set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
Owner.AddChild(instance);
|
||||
ActiveComponents[skillData.Name] = instance;
|
||||
}
|
||||
|
||||
public void RemoveSkill(string skillName)
|
||||
{
|
||||
if (!ActiveComponents.TryGetValue(skillName, out var component))
|
||||
return;
|
||||
|
||||
var inst = (Node)component;
|
||||
if (IsInstanceValid(inst))
|
||||
inst.QueueFree();
|
||||
|
||||
var skills = _gameManager.GetUnlockedSkills();
|
||||
foreach (SkillData s in skills)
|
||||
{
|
||||
if (s.Name == skillName)
|
||||
{
|
||||
s.IsActive = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ActiveComponents.Remove(skillName);
|
||||
}
|
||||
|
||||
public void ApplyUnlockedSkills()
|
||||
{
|
||||
foreach (var sd in AvailableSkills)
|
||||
{
|
||||
if (_gameManager.IsSkillUnlocked(sd))
|
||||
{
|
||||
GD.Print("Applying skill: ", sd.Name);
|
||||
CallDeferred(MethodName.AddSkill, sd);
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveSkill(sd.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SkillData GetSkillByName(string skillName)
|
||||
{
|
||||
foreach (var sd in AvailableSkills)
|
||||
if (sd.Name == skillName) return sd;
|
||||
return null;
|
||||
}
|
||||
|
||||
public void ActivateSkill(SkillData skill)
|
||||
{
|
||||
if (!ActiveComponents.ContainsKey(skill.Name))
|
||||
{
|
||||
AddSkill(skill);
|
||||
skill.IsActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void DeactivateSkill(SkillData skill)
|
||||
{
|
||||
if (ActiveComponents.ContainsKey(skill.Name))
|
||||
{
|
||||
RemoveSkill(skill.Name);
|
||||
skill.IsActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void ToggleSkillActivation(SkillData skill)
|
||||
{
|
||||
if (skill == null) return;
|
||||
|
||||
if (ActiveComponents.ContainsKey(skill.Name))
|
||||
DeactivateSkill(skill);
|
||||
else
|
||||
ActivateSkill(skill);
|
||||
}
|
||||
}
|
1
scripts/SkillManager.cs.uid
Normal file
1
scripts/SkillManager.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://di572axt0c3s8
|
102
scripts/UI/AudioSettings.cs
Normal file
102
scripts/UI/AudioSettings.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.UI;
|
||||
|
||||
public partial class AudioSettings : Node
|
||||
{
|
||||
[Export] public Slider MasterVolumeSlider { get; set; }
|
||||
[Export] public Slider MusicVolumeSlider { get; set; }
|
||||
[Export] public Slider SfxVolumeSlider { get; set; }
|
||||
[Export] public Control AudioSettingsControl { get; set; }
|
||||
[Export] public float MuteThreshold { get; set; } = -20f;
|
||||
|
||||
private UIManager _uiManager;
|
||||
private ConfigFileHandler _configFileHandler;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_uiManager = GetNode<UIManager>("/root/UIManager");
|
||||
_configFileHandler = GetNode<ConfigFileHandler>("/root/ConfigFileHandler");
|
||||
Initialize();
|
||||
MasterVolumeSlider.ValueChanged += OnMasterVolumeChanged;
|
||||
MusicVolumeSlider.ValueChanged += OnMusicVolumeChanged;
|
||||
SfxVolumeSlider.ValueChanged += OnSfxVolumeChanged;
|
||||
}
|
||||
|
||||
public override void _UnhandledInput(InputEvent @event)
|
||||
{
|
||||
if (!@event.IsActionReleased("ui_cancel")) return;
|
||||
if (!_uiManager.IsScreenOnTop(AudioSettingsControl)) return;
|
||||
|
||||
SaveSettings();
|
||||
_uiManager.PopScreen();
|
||||
}
|
||||
|
||||
private void OnSfxVolumeChanged(double value)
|
||||
{
|
||||
AudioServer.SetBusVolumeDb(AudioServer.GetBusIndex("sfx"), (float)value);
|
||||
HandleMute(AudioServer.GetBusIndex("sfx"), (float)value);
|
||||
}
|
||||
|
||||
private void OnMusicVolumeChanged(double value)
|
||||
{
|
||||
AudioServer.SetBusVolumeDb(AudioServer.GetBusIndex("music"), (float)value);
|
||||
HandleMute(AudioServer.GetBusIndex("music"), (float)value);
|
||||
}
|
||||
|
||||
private void OnMasterVolumeChanged(double value)
|
||||
{
|
||||
AudioServer.SetBusVolumeDb(AudioServer.GetBusIndex("Master"), (float)value);
|
||||
HandleMute(AudioServer.GetBusIndex("Master"), (float)value);
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
var volumeDb = AudioServer.GetBusVolumeDb(AudioServer.GetBusIndex("Master"));
|
||||
MasterVolumeSlider.Value = volumeDb;
|
||||
MasterVolumeSlider.MinValue = MuteThreshold;
|
||||
MasterVolumeSlider.MaxValue = 0f;
|
||||
|
||||
var musicVolumeDb = AudioServer.GetBusVolumeDb(AudioServer.GetBusIndex("music"));
|
||||
MusicVolumeSlider.Value = musicVolumeDb;
|
||||
MusicVolumeSlider.MinValue = MuteThreshold;
|
||||
MusicVolumeSlider.MaxValue = 0f;
|
||||
|
||||
var sfxVolumeDb = AudioServer.GetBusVolumeDb(AudioServer.GetBusIndex("sfx"));
|
||||
SfxVolumeSlider.Value = sfxVolumeDb;
|
||||
SfxVolumeSlider.MinValue = MuteThreshold;
|
||||
SfxVolumeSlider.MaxValue = 0f;
|
||||
}
|
||||
|
||||
private void HandleMute(int busIndex, float value)
|
||||
{
|
||||
AudioServer.SetBusMute(busIndex, value <= MuteThreshold);
|
||||
}
|
||||
|
||||
private void SaveSettings()
|
||||
{
|
||||
var settingsConfig = _configFileHandler.SettingsConfig;
|
||||
settingsConfig.SetValue("audio_settings", "master_volume", MasterVolumeSlider.Value);
|
||||
settingsConfig.SetValue("audio_settings", "music_volume", MusicVolumeSlider.Value);
|
||||
settingsConfig.SetValue("audio_settings", "sfx_volume", SfxVolumeSlider.Value);
|
||||
settingsConfig.SetValue("audio_settings", "mute_threshold", MuteThreshold);
|
||||
settingsConfig.Save(ConfigFileHandler.SettingsPath);
|
||||
}
|
||||
|
||||
private void LoadSettings()
|
||||
{
|
||||
var settingsConfig = _configFileHandler.SettingsConfig;
|
||||
if (!settingsConfig.HasSection("audio_settings")) return;
|
||||
|
||||
var masterVolume = (float)settingsConfig.GetValue("audio_settings", "master_volume", MasterVolumeSlider.Value);
|
||||
var musicVolume = (float)settingsConfig.GetValue("audio_settings", "music_volume", MusicVolumeSlider.Value);
|
||||
var sfxVolume = (float)settingsConfig.GetValue("audio_settings", "sfx_volume", SfxVolumeSlider.Value);
|
||||
var muteThreshold = (float)settingsConfig.GetValue("audio_settings", "mute_threshold", MuteThreshold);
|
||||
|
||||
MasterVolumeSlider.Value = masterVolume;
|
||||
MusicVolumeSlider.Value = musicVolume;
|
||||
SfxVolumeSlider.Value = sfxVolume;
|
||||
MuteThreshold = muteThreshold;
|
||||
}
|
||||
}
|
1
scripts/UI/AudioSettings.cs.uid
Normal file
1
scripts/UI/AudioSettings.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://g61qqsymqfxd
|
81
scripts/UI/ChargeProgressBar.cs
Normal file
81
scripts/UI/ChargeProgressBar.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures.scripts.components;
|
||||
using Mr.BrickAdventures.scripts.Resources;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.UI;
|
||||
|
||||
public partial class ChargeProgressBar : Node
|
||||
{
|
||||
[Export] public ProgressBar ProgressBar { get; set; }
|
||||
[Export] public BrickThrowComponent ThrowComponent { get; set; }
|
||||
|
||||
private ChargeThrowInputResource _throwInput;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
Owner.ChildEnteredTree += OnNodeEntered;
|
||||
ProgressBar.Hide();
|
||||
SetupDependencies();
|
||||
}
|
||||
|
||||
private void OnNodeEntered(Node node)
|
||||
{
|
||||
if (node is not BrickThrowComponent throwComponent || ThrowComponent != null) return;
|
||||
ThrowComponent = throwComponent;
|
||||
SetupDependencies();
|
||||
}
|
||||
|
||||
private void SetupDependencies()
|
||||
{
|
||||
if (ThrowComponent.ThrowInputBehavior is ChargeThrowInputResource throwInput)
|
||||
{
|
||||
_throwInput = throwInput;
|
||||
}
|
||||
else
|
||||
{
|
||||
_throwInput = null;
|
||||
}
|
||||
|
||||
if (_throwInput == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_throwInput.SupportsCharging())
|
||||
{
|
||||
ProgressBar.Hide();
|
||||
return;
|
||||
}
|
||||
|
||||
SetupProgressBar();
|
||||
|
||||
_throwInput.ChargeStarted += OnChargeStarted;
|
||||
_throwInput.ChargeStopped += OnChargeStopped;
|
||||
_throwInput.ChargeUpdated += OnChargeUpdated;
|
||||
}
|
||||
|
||||
private void SetupProgressBar()
|
||||
{
|
||||
ProgressBar.MinValue = _throwInput.MinPower;
|
||||
ProgressBar.MaxValue = _throwInput.MaxPower;
|
||||
ProgressBar.Value = _throwInput.MinPower;
|
||||
ProgressBar.Step = 0.01f;
|
||||
ProgressBar.Hide();
|
||||
}
|
||||
|
||||
private void OnChargeStarted()
|
||||
{
|
||||
ProgressBar.Show();
|
||||
}
|
||||
|
||||
private void OnChargeStopped()
|
||||
{
|
||||
ProgressBar.Hide();
|
||||
}
|
||||
|
||||
private void OnChargeUpdated(float chargeRatio)
|
||||
{
|
||||
ProgressBar.Value = chargeRatio;
|
||||
ProgressBar.Show();
|
||||
}
|
||||
}
|
1
scripts/UI/ChargeProgressBar.cs.uid
Normal file
1
scripts/UI/ChargeProgressBar.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dev2q1228otm2
|
23
scripts/UI/Credits.cs
Normal file
23
scripts/UI/Credits.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.UI;
|
||||
|
||||
public partial class Credits : Control
|
||||
{
|
||||
private UIManager _uiManager;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_uiManager = GetNode<UIManager>("/root/UIManager");
|
||||
}
|
||||
|
||||
public override void _UnhandledInput(InputEvent @event)
|
||||
{
|
||||
if (!@event.IsActionPressed("ui_cancel")) return;
|
||||
if (_uiManager != null && _uiManager.IsScreenOnTop(this))
|
||||
{
|
||||
_uiManager.PopScreen();
|
||||
}
|
||||
}
|
||||
}
|
1
scripts/UI/Credits.cs.uid
Normal file
1
scripts/UI/Credits.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://daevj4uootmcw
|
39
scripts/UI/GameOverScreen.cs
Normal file
39
scripts/UI/GameOverScreen.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.UI;
|
||||
|
||||
public partial class GameOverScreen : Node
|
||||
{
|
||||
[Export] public Control GameOverPanel { get; set; }
|
||||
[Export] public Button RestartButton { get; set; }
|
||||
[Export] public Button MainMenuButton { get; set; }
|
||||
[Export] public PackedScene MainMenuScene { get; set; }
|
||||
|
||||
private GameManager _gameManager;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_gameManager = GetNode<GameManager>("/root/GameManager");
|
||||
RestartButton.Pressed += OnRestartClicked;
|
||||
MainMenuButton.Pressed += OnMainMenuClicked;
|
||||
}
|
||||
|
||||
private void OnMainMenuClicked()
|
||||
{
|
||||
_gameManager.ResetPlayerState();
|
||||
GetTree().ChangeSceneToPacked(MainMenuScene);
|
||||
}
|
||||
|
||||
private void OnRestartClicked()
|
||||
{
|
||||
_gameManager.RestartGame();
|
||||
}
|
||||
|
||||
public void OnPlayerDeath()
|
||||
{
|
||||
if (_gameManager == null || _gameManager.GetLives() != 0) return;
|
||||
|
||||
GameOverPanel.Show();
|
||||
}
|
||||
}
|
1
scripts/UI/GameOverScreen.cs.uid
Normal file
1
scripts/UI/GameOverScreen.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://u4qfsx4w72dv
|
67
scripts/UI/MainMenu.cs
Normal file
67
scripts/UI/MainMenu.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.UI;
|
||||
|
||||
public partial class MainMenu : Node
|
||||
{
|
||||
[Export] public Control MainMenuControl { get; set; }
|
||||
[Export] public Button NewGameButton { get; set; }
|
||||
[Export] public Button ContinueButton { get; set; }
|
||||
[Export] public Button SettingsButton { get; set; }
|
||||
[Export] public Button CreditsButton { get; set; }
|
||||
[Export] public Button ExitButton { get; set; }
|
||||
[Export] public Label VersionLabel { get; set; }
|
||||
[Export] public Control SettingsControl { get; set; }
|
||||
[Export] public Control CreditsControl { get; set; }
|
||||
|
||||
private SaveSystem _saveSystem;
|
||||
private GameManager _gameManager;
|
||||
private UIManager _uiManager;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_saveSystem = GetNode<SaveSystem>("/root/SaveSystem");
|
||||
_gameManager = GetNode<GameManager>("/root/GameManager");
|
||||
_uiManager = GetNode<UIManager>("/root/UIManager");
|
||||
|
||||
NewGameButton.Pressed += OnNewGamePressed;
|
||||
ContinueButton.Pressed += OnContinuePressed;
|
||||
SettingsButton.Pressed += OnSettingsPressed;
|
||||
CreditsButton.Pressed += OnCreditsPressed;
|
||||
ExitButton.Pressed += OnExitPressed;
|
||||
|
||||
VersionLabel.Text = $"v. {ProjectSettings.GetSetting("application/config/version")}";
|
||||
ContinueButton.Disabled = !_saveSystem.CheckSaveExists();
|
||||
|
||||
if (_saveSystem.CheckSaveExists())
|
||||
ContinueButton.GrabFocus();
|
||||
else
|
||||
NewGameButton.GrabFocus();
|
||||
}
|
||||
|
||||
private void OnExitPressed()
|
||||
{
|
||||
_gameManager.QuitGame();
|
||||
}
|
||||
|
||||
private void OnCreditsPressed()
|
||||
{
|
||||
_uiManager.PushScreen(CreditsControl);
|
||||
}
|
||||
|
||||
private void OnSettingsPressed()
|
||||
{
|
||||
_uiManager.PushScreen(SettingsControl);
|
||||
}
|
||||
|
||||
private void OnContinuePressed()
|
||||
{
|
||||
_gameManager.ContinueGame();
|
||||
}
|
||||
|
||||
private void OnNewGamePressed()
|
||||
{
|
||||
_gameManager.StartNewGame();
|
||||
}
|
||||
}
|
1
scripts/UI/MainMenu.cs.uid
Normal file
1
scripts/UI/MainMenu.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bna3ggr6n7ycr
|
142
scripts/UI/Marketplace.cs
Normal file
142
scripts/UI/Marketplace.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
using System.Collections.Generic;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
using Mr.BrickAdventures.scripts.components;
|
||||
using Mr.BrickAdventures.scripts.Resources;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.UI;
|
||||
|
||||
public partial class Marketplace : Node
|
||||
{
|
||||
[Export] public Array<SkillData> Skills { get; set; } = [];
|
||||
[Export] public GridContainer ToUnlockGrid { get; set; }
|
||||
[Export] public GridContainer UnlockedGrid { get; set; }
|
||||
[Export] public Font Font { get; set; }
|
||||
[Export] public SkillUnlockedComponent SkillUnlockedComponent { get; set; }
|
||||
[Export] public Array<Node> ComponentsToDisable { get; set; } = [];
|
||||
[Export] public PackedScene MarketplaceButtonScene { get; set; }
|
||||
[Export] public PackedScene SkillButtonScene { get; set; }
|
||||
|
||||
private GameManager _gameManager;
|
||||
private readonly List<Button> _unlockButtons = [];
|
||||
private readonly List<SkillButton> _skillButtons = [];
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
var skillsToUnlock = new List<SkillData>();
|
||||
|
||||
foreach (var skill in Skills) skillsToUnlock.Add(skill);
|
||||
|
||||
foreach (var skill in skillsToUnlock) CreateUpgradeButton(skill);
|
||||
|
||||
var unlockedSkills = _gameManager.GetUnlockedSkills();
|
||||
foreach (var skill in unlockedSkills) CreateSkillButton(skill);
|
||||
|
||||
SkillUnlockedComponent.SkillUnlocked += OnSkillUnlocked;
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
SkillUnlockedComponent.SkillUnlocked -= OnSkillUnlocked;
|
||||
}
|
||||
|
||||
public override void _Input(InputEvent @event)
|
||||
{
|
||||
var root = Owner as Control;
|
||||
|
||||
if (!@event.IsActionPressed("show_marketplace")) return;
|
||||
|
||||
if (root != null && root.IsVisible())
|
||||
{
|
||||
root.Hide();
|
||||
foreach (var c in ComponentsToDisable) c.ProcessMode = ProcessModeEnum.Inherit;
|
||||
}
|
||||
else
|
||||
{
|
||||
root?.Show();
|
||||
foreach (var c in ComponentsToDisable) c.ProcessMode = ProcessModeEnum.Disabled;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetButtonText(SkillData skill)
|
||||
{
|
||||
return $"{Tr(skill.Name)} {skill.Cost}";
|
||||
}
|
||||
|
||||
private void OnSkillUnlocked(SkillData skill)
|
||||
{
|
||||
if (_skillButtons.Count == 0) CreateSkillButton(skill);
|
||||
|
||||
foreach (var btn in _skillButtons)
|
||||
{
|
||||
if (btn.Data.IsActive)
|
||||
btn.Activate();
|
||||
else
|
||||
btn.Deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateSkillButton(SkillData skill)
|
||||
{
|
||||
var button = SkillButtonScene.Instantiate<SkillButton>();
|
||||
button.Data = skill;
|
||||
button.Setup();
|
||||
button.Pressed += () => OnSkillButtonPressed(button);
|
||||
button.Activate();
|
||||
|
||||
_skillButtons.Add(button);
|
||||
UnlockedGrid.AddChild(button);
|
||||
UnlockedGrid.QueueSort();
|
||||
}
|
||||
|
||||
private void CreateUpgradeButton(SkillData skill)
|
||||
{
|
||||
var button = MarketplaceButtonScene.Instantiate<MarketplaceButton>();
|
||||
button.Text = GetButtonText(skill);
|
||||
button.Data = skill;
|
||||
button.Icon = skill.Icon;
|
||||
button.Pressed += () => OnUpgradeButtonPressed(skill);
|
||||
|
||||
_unlockButtons.Add(button);
|
||||
UnlockedGrid.AddChild(button);
|
||||
UnlockedGrid.QueueSort();
|
||||
}
|
||||
|
||||
private void OnUpgradeButtonPressed(SkillData skill)
|
||||
{
|
||||
if (_gameManager.IsSkillUnlocked(skill))
|
||||
{
|
||||
if (skill.Level < skill.MaxLevel)
|
||||
{
|
||||
SkillUnlockedComponent.TryUpgradeSkill(skill);
|
||||
if (!skill.IsActive) SkillUnlockedComponent.SkillManager.ToggleSkillActivation(skill);
|
||||
}
|
||||
else
|
||||
{
|
||||
SkillUnlockedComponent.SkillManager.ToggleSkillActivation(skill);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SkillUnlockedComponent.TryUnlockSkill(skill);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveButton(SkillData skill)
|
||||
{
|
||||
foreach (var node in ToUnlockGrid.GetChildren())
|
||||
{
|
||||
var child = (Button)node;
|
||||
if (child.Text != GetButtonText(skill)) continue;
|
||||
|
||||
child.QueueFree();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSkillButtonPressed(SkillButton button)
|
||||
{
|
||||
SkillUnlockedComponent.SkillManager.ToggleSkillActivation(button.Data);
|
||||
}
|
||||
}
|
1
scripts/UI/Marketplace.cs.uid
Normal file
1
scripts/UI/Marketplace.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bnc16gndpl87i
|
62
scripts/UI/MarketplaceButton.cs
Normal file
62
scripts/UI/MarketplaceButton.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
using Mr.BrickAdventures.scripts.components;
|
||||
using Mr.BrickAdventures.scripts.Resources;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.UI;
|
||||
|
||||
public partial class MarketplaceButton : Button
|
||||
{
|
||||
[Export] public SkillData Data { get; set; }
|
||||
[Export] public Texture2D UnlockedSkillIcon { get; set; }
|
||||
[Export] public Texture2D LockedSkillIcon { get; set; }
|
||||
[Export] public Container SkillLevelContainer { get; set; }
|
||||
|
||||
private GameManager _gameManager;
|
||||
private SkillUnlockedComponent _skillUnlockedComponent;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_gameManager = GetNode<GameManager>("/root/GameManager");
|
||||
|
||||
Setup();
|
||||
var player = _gameManager.Player;
|
||||
|
||||
var skillUnlockerComponent = player?.GetNodeOrNull<SkillUnlockedComponent>("SkillUnlockerComponent");
|
||||
if (skillUnlockerComponent == null) return;
|
||||
|
||||
skillUnlockerComponent.SkillUnlocked += OnSkillUnlock;
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
_skillUnlockedComponent.SkillUnlocked -= OnSkillUnlock;
|
||||
}
|
||||
|
||||
private void Setup()
|
||||
{
|
||||
if (Data == null) return;
|
||||
|
||||
for (var i = 0; i < Data.MaxLevel; i++)
|
||||
{
|
||||
var icon = new TextureRect()
|
||||
{
|
||||
Texture = i < Data.Level ? UnlockedSkillIcon : LockedSkillIcon,
|
||||
};
|
||||
SkillLevelContainer.AddChild(icon);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSkillUnlock(SkillData skill)
|
||||
{
|
||||
if (skill.Name != Data.Name) return;
|
||||
|
||||
for (var i = 0; i < Data.MaxLevel; i++)
|
||||
{
|
||||
var icon = SkillLevelContainer.GetChildOrNull<TextureRect>(i);
|
||||
if (icon == null) continue;
|
||||
icon.Texture = i < Data.Level ? UnlockedSkillIcon : LockedSkillIcon;
|
||||
Disabled = i >= Data.Level;
|
||||
}
|
||||
}
|
||||
}
|
1
scripts/UI/MarketplaceButton.cs.uid
Normal file
1
scripts/UI/MarketplaceButton.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://vokgv56bjpf1
|
67
scripts/UI/PauseMenu.cs
Normal file
67
scripts/UI/PauseMenu.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.UI;
|
||||
|
||||
public partial class PauseMenu : Node
|
||||
{
|
||||
[Export] public Control PauseMenuControl { get; set; }
|
||||
[Export] public Control SettingsControl { get; set; }
|
||||
[Export] public Button ResumeButton { get; set; }
|
||||
[Export] public Button MainMenuButton { get; set; }
|
||||
[Export] public Button QuitButton { get; set; }
|
||||
[Export] public Button SettingsButton { get; set; }
|
||||
[Export] public PackedScene MainMenuScene { get; set; }
|
||||
|
||||
private GameManager _gameManager;
|
||||
private UIManager _uiManager;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_gameManager = GetNode<GameManager>("/root/GameManager");
|
||||
_uiManager = GetNode<UIManager>("/root/UIManager");
|
||||
|
||||
ResumeButton.Pressed += OnResumePressed;
|
||||
MainMenuButton.Pressed += OnMainMenuPressed;
|
||||
QuitButton.Pressed += OnQuitPressed;
|
||||
SettingsButton.Pressed += OnSettingsPressed;
|
||||
|
||||
PauseMenuControl.Hide();
|
||||
}
|
||||
|
||||
public override void _UnhandledInput(InputEvent @event)
|
||||
{
|
||||
if (!@event.IsActionPressed("pause")) return;
|
||||
if (_uiManager.IsVisibleOnStack(PauseMenuControl))
|
||||
OnResumePressed();
|
||||
else
|
||||
{
|
||||
_uiManager.PushScreen(PauseMenuControl);
|
||||
_gameManager.PauseGame();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSettingsPressed()
|
||||
{
|
||||
_uiManager.PushScreen(SettingsControl);
|
||||
_gameManager.PauseGame();
|
||||
}
|
||||
|
||||
private void OnQuitPressed()
|
||||
{
|
||||
_gameManager.QuitGame();
|
||||
}
|
||||
|
||||
private void OnMainMenuPressed()
|
||||
{
|
||||
_gameManager.ResumeGame();
|
||||
_gameManager.ResetCurrentSessionState();
|
||||
GetTree().ChangeSceneToPacked(MainMenuScene);
|
||||
}
|
||||
|
||||
private void OnResumePressed()
|
||||
{
|
||||
_uiManager.PopScreen();
|
||||
_gameManager.ResumeGame();
|
||||
}
|
||||
}
|
1
scripts/UI/PauseMenu.cs.uid
Normal file
1
scripts/UI/PauseMenu.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cakgxndurgfa3
|
60
scripts/UI/SettingsMenu.cs
Normal file
60
scripts/UI/SettingsMenu.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using Godot;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.UI;
|
||||
|
||||
public partial class SettingsMenu : Node
|
||||
{
|
||||
[Export] public Control InputSettingsControl { get; set; }
|
||||
[Export] public Control AudioSettingsControl { get; set; }
|
||||
[Export] public Control DisplaySettingsControl { get; set; }
|
||||
[Export] public Control GameplaySettingsControl { get; set; }
|
||||
[Export] public Control SettingsMenuControl { get; set; }
|
||||
[Export] public Button InputSettingsButton { get; set; }
|
||||
[Export] public Button AudioSettingsButton { get; set; }
|
||||
[Export] public Button DisplaySettingsButton { get; set; }
|
||||
[Export] public Button GameplaySettingsButton { get; set; }
|
||||
|
||||
private UIManager _uiManager;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_uiManager = GetNode<UIManager>("/root/UIManager");
|
||||
|
||||
InputSettingsButton.Pressed += OnInputSettingsPressed;
|
||||
AudioSettingsButton.Pressed += OnAudioSettingsPressed;
|
||||
DisplaySettingsButton.Pressed += OnDisplaySettingsPressed;
|
||||
GameplaySettingsButton.Pressed += OnGameplaySettingsPressed;
|
||||
|
||||
InputSettingsControl.Hide();
|
||||
AudioSettingsControl.Hide();
|
||||
DisplaySettingsControl.Hide();
|
||||
GameplaySettingsControl.Hide();
|
||||
}
|
||||
|
||||
public override void _UnhandledInput(InputEvent @event)
|
||||
{
|
||||
if (!@event.IsActionPressed("ui_cancel")) return;
|
||||
if (_uiManager.IsScreenOnTop(SettingsMenuControl)) _uiManager.PopScreen();
|
||||
}
|
||||
|
||||
private void OnInputSettingsPressed()
|
||||
{
|
||||
_uiManager.PushScreen(InputSettingsControl);
|
||||
}
|
||||
|
||||
private void OnAudioSettingsPressed()
|
||||
{
|
||||
_uiManager.PushScreen(AudioSettingsControl);
|
||||
}
|
||||
|
||||
private void OnDisplaySettingsPressed()
|
||||
{
|
||||
_uiManager.PushScreen(DisplaySettingsControl);
|
||||
}
|
||||
|
||||
private void OnGameplaySettingsPressed()
|
||||
{
|
||||
_uiManager.PushScreen(GameplaySettingsControl);
|
||||
}
|
||||
}
|
1
scripts/UI/SettingsMenu.cs.uid
Normal file
1
scripts/UI/SettingsMenu.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://i8j47qnrytuo
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user