diff --git a/Mr. Brick Adventures.csproj b/Mr. Brick Adventures.csproj
index 57454f6..4c4a7f5 100644
--- a/Mr. Brick Adventures.csproj
+++ b/Mr. Brick Adventures.csproj
@@ -4,146 +4,10 @@
true
Mr.BrickAdventures
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/common/AppRoot.cs b/common/AppRoot.cs
new file mode 100644
index 0000000..8a059b3
--- /dev/null
+++ b/common/AppRoot.cs
@@ -0,0 +1,34 @@
+using Chickensoft.AutoInject;
+using Chickensoft.Introspection;
+using Godot;
+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, IProvide, IProvide, IProvide
+{
+ public override void _Notification(int what) => this.Notify(what);
+
+ private readonly SaveClient _save = new("user://savegame.save", version: 2);
+ private readonly PlayerRepository _players = new();
+ private readonly LevelRepository _levels = new();
+ private SaveService _saveService = null!;
+
+ PlayerRepository IProvide.Value() => _players;
+ LevelRepository IProvide.Value() => _levels;
+ SaveClient IProvide.Value() => _save;
+ SaveService IProvide.Value() => _saveService;
+
+
+ public void OnReady()
+ {
+ _saveService = new SaveService(_players, _levels, _save);
+
+ _saveService.TryLoad();
+
+ this.Provide();
+ }
+}
\ No newline at end of file
diff --git a/common/AppRoot.cs.uid b/common/AppRoot.cs.uid
new file mode 100644
index 0000000..e90f264
--- /dev/null
+++ b/common/AppRoot.cs.uid
@@ -0,0 +1 @@
+uid://dg2l7cw6da4vb
diff --git a/data/ConfigClient.cs b/data/ConfigClient.cs
new file mode 100644
index 0000000..5dbd0ac
--- /dev/null
+++ b/data/ConfigClient.cs
@@ -0,0 +1,6 @@
+namespace Mr.BrickAdventures.data;
+
+public class ConfigClient
+{
+
+}
\ No newline at end of file
diff --git a/data/ConfigClient.cs.uid b/data/ConfigClient.cs.uid
new file mode 100644
index 0000000..4686182
--- /dev/null
+++ b/data/ConfigClient.cs.uid
@@ -0,0 +1 @@
+uid://d3s774lnoljcu
diff --git a/data/SaveClient.cs b/data/SaveClient.cs
new file mode 100644
index 0000000..ac324b5
--- /dev/null
+++ b/data/SaveClient.cs
@@ -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(s.Unlocked) },
+ { "completed", new Array(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)ul is { } a1 ? a1.ToArray() : [0],
+ Completed = d.TryGetValue("completed", out var cl) && (Array)cl is { } a2 ? a2.ToArray() : [],
+ };
+}
+
+public record SaveSnapshot {
+ public required PlayerState PlayerState { get; init; }
+ public required LevelState LevelState { get; init; }
+}
\ No newline at end of file
diff --git a/data/SaveClient.cs.uid b/data/SaveClient.cs.uid
new file mode 100644
index 0000000..d334625
--- /dev/null
+++ b/data/SaveClient.cs.uid
@@ -0,0 +1 @@
+uid://bsf45t7m2wa07
diff --git a/features/ui/hud/Hud.cs b/features/ui/hud/Hud.cs
new file mode 100644
index 0000000..99a08aa
--- /dev/null
+++ b/features/ui/hud/Hud.cs
@@ -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!;
+ [Export] public Label CoinsLabel { get; set; } = null!;
+ [Export] public ProgressBar HealthBar { get; set; } = null!;
+ [Export] public Label LivesLabel { get; set; } = null!;
+
+ [Dependency] public PlayerRepository Player => this.DependOn();
+
+ 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; };
+ }
+ }
+}
\ No newline at end of file
diff --git a/features/ui/hud/Hud.cs.uid b/features/ui/hud/Hud.cs.uid
new file mode 100644
index 0000000..5014c39
--- /dev/null
+++ b/features/ui/hud/Hud.cs.uid
@@ -0,0 +1 @@
+uid://c1uwe5e1cfdxl
diff --git a/features/ui/menus/MainMenu.cs b/features/ui/menus/MainMenu.cs
new file mode 100644
index 0000000..d3bff9e
--- /dev/null
+++ b/features/ui/menus/MainMenu.cs
@@ -0,0 +1,58 @@
+using Chickensoft.AutoInject;
+using Chickensoft.Introspection;
+using Godot;
+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);
+
+ [Export] public Control MainMenuControl { get; set; } = null!;
+ [Export] public Button NewGameButton { get; set; } = null!;
+ [Export] public Button ContinueButton { get; set; } = null!;
+ [Export] public Button SettingsButton { get; set; } = null!;
+ [Export] public Button CreditsButton { get; set; } = null!;
+ [Export] public Button ExitButton { get; set; } = null!;
+ [Export] public Label VersionLabel { get; set; } = null!;
+ [Export] public Control SettingsControl { get; set; } = null!;
+ [Export] public Control CreditsControl { get; set; } = null!;
+ [Export] public PackedScene FirstLevelScene { get; set; } = null!;
+
+ [Dependency] public SaveService Save => this.DependOn();
+ [Dependency] public LevelRepository Levels => this.DependOn();
+
+ 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);
+ GetTree().ChangeSceneToPacked(FirstLevelScene);
+ }
+
+ private void OnContinuePressed() {
+ if (!Save.TryLoad()) {
+ // Fallback: start new game
+ OnNewGamePressed();
+ return;
+ }
+ GetTree().ChangeSceneToPacked(FirstLevelScene);
+ }
+}
\ No newline at end of file
diff --git a/features/ui/menus/MainMenu.cs.uid b/features/ui/menus/MainMenu.cs.uid
new file mode 100644
index 0000000..1f5ab15
--- /dev/null
+++ b/features/ui/menus/MainMenu.cs.uid
@@ -0,0 +1 @@
+uid://fcsg0e8s36in
diff --git a/features/ui/menus/PauseMenu.cs b/features/ui/menus/PauseMenu.cs
new file mode 100644
index 0000000..85f8e84
--- /dev/null
+++ b/features/ui/menus/PauseMenu.cs
@@ -0,0 +1,6 @@
+namespace Mr.BrickAdventures.features.ui.menus;
+
+public class PauseMenu
+{
+
+}
\ No newline at end of file
diff --git a/features/ui/menus/PauseMenu.cs.uid b/features/ui/menus/PauseMenu.cs.uid
new file mode 100644
index 0000000..511293d
--- /dev/null
+++ b/features/ui/menus/PauseMenu.cs.uid
@@ -0,0 +1 @@
+uid://bwgs02wcfnm8u
diff --git a/objects/ui/main_menu.tscn b/features/ui/menus/main_menu.tscn
similarity index 73%
rename from objects/ui/main_menu.tscn
rename to features/ui/menus/main_menu.tscn
index 3bdf935..ae61493 100644
--- a/objects/ui/main_menu.tscn
+++ b/features/ui/menus/main_menu.tscn
@@ -1,25 +1,25 @@
[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" node_paths=PackedStringArray("MainMenuControl", "NewGameButton", "ContinueButton", "SettingsButton", "CreditsButton", "ExitButton", "VersionLabel")]
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")
+MainMenuControl = NodePath(".")
+NewGameButton = NodePath("PanelContainer/MarginContainer/VBoxContainer/NewGameButton")
+ContinueButton = NodePath("PanelContainer/MarginContainer/VBoxContainer/ContinueButton")
+SettingsButton = NodePath("PanelContainer/MarginContainer/VBoxContainer/SettingsButton")
+CreditsButton = NodePath("PanelContainer/MarginContainer/VBoxContainer/CreditsButton")
+ExitButton = NodePath("PanelContainer/MarginContainer/VBoxContainer/QuitButton")
+VersionLabel = NodePath("PanelContainer/MarginContainer/VBoxContainer/version")
[node name="PanelContainer" type="PanelContainer" parent="."]
layout_mode = 1
diff --git a/features/ui/settings/AudioSettings.cs b/features/ui/settings/AudioSettings.cs
new file mode 100644
index 0000000..7d16574
--- /dev/null
+++ b/features/ui/settings/AudioSettings.cs
@@ -0,0 +1,6 @@
+namespace Mr.BrickAdventures.features.ui.settings;
+
+public class AudioSettings
+{
+
+}
\ No newline at end of file
diff --git a/features/ui/settings/AudioSettings.cs.uid b/features/ui/settings/AudioSettings.cs.uid
new file mode 100644
index 0000000..96fb589
--- /dev/null
+++ b/features/ui/settings/AudioSettings.cs.uid
@@ -0,0 +1 @@
+uid://bnj36dhskiem
diff --git a/game/repositories/LevelRepository.cs b/game/repositories/LevelRepository.cs
new file mode 100644
index 0000000..35900b3
--- /dev/null
+++ b/game/repositories/LevelRepository.cs
@@ -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 Unlocked { get; } = new() { 0 };
+ public HashSet Completed { get; } = new();
+
+ public event Action? 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 = [];
+}
\ No newline at end of file
diff --git a/game/repositories/LevelRepository.cs.uid b/game/repositories/LevelRepository.cs.uid
new file mode 100644
index 0000000..51ccdf5
--- /dev/null
+++ b/game/repositories/LevelRepository.cs.uid
@@ -0,0 +1 @@
+uid://d0604lmwpadt5
diff --git a/game/repositories/PlayerRepository.cs b/game/repositories/PlayerRepository.cs
new file mode 100644
index 0000000..14eb6c5
--- /dev/null
+++ b/game/repositories/PlayerRepository.cs
@@ -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? CoinsChanged;
+ public event Action? 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; }
\ No newline at end of file
diff --git a/game/repositories/PlayerRepository.cs.uid b/game/repositories/PlayerRepository.cs.uid
new file mode 100644
index 0000000..0aa5531
--- /dev/null
+++ b/game/repositories/PlayerRepository.cs.uid
@@ -0,0 +1 @@
+uid://d1dijkt574x4b
diff --git a/game/repositories/SessionRepository.cs b/game/repositories/SessionRepository.cs
new file mode 100644
index 0000000..93781b6
--- /dev/null
+++ b/game/repositories/SessionRepository.cs
@@ -0,0 +1,6 @@
+namespace Mr.BrickAdventures.game.repositories;
+
+public sealed class SessionRepository
+{
+
+}
\ No newline at end of file
diff --git a/game/repositories/SessionRepository.cs.uid b/game/repositories/SessionRepository.cs.uid
new file mode 100644
index 0000000..32a435b
--- /dev/null
+++ b/game/repositories/SessionRepository.cs.uid
@@ -0,0 +1 @@
+uid://b550hcqugygv0
diff --git a/game/repositories/SettingsRepository.cs b/game/repositories/SettingsRepository.cs
new file mode 100644
index 0000000..289a6b9
--- /dev/null
+++ b/game/repositories/SettingsRepository.cs
@@ -0,0 +1,6 @@
+namespace Mr.BrickAdventures.game.repositories;
+
+public class SettingsRepository
+{
+
+}
\ No newline at end of file
diff --git a/game/repositories/SettingsRepository.cs.uid b/game/repositories/SettingsRepository.cs.uid
new file mode 100644
index 0000000..f6b01ee
--- /dev/null
+++ b/game/repositories/SettingsRepository.cs.uid
@@ -0,0 +1 @@
+uid://bk11iduo7bg2h
diff --git a/game/repositories/SkillsRepository.cs b/game/repositories/SkillsRepository.cs
new file mode 100644
index 0000000..dbe64be
--- /dev/null
+++ b/game/repositories/SkillsRepository.cs
@@ -0,0 +1,6 @@
+namespace Mr.BrickAdventures.game.repositories;
+
+public class SkillsRepository
+{
+
+}
\ No newline at end of file
diff --git a/game/repositories/SkillsRepository.cs.uid b/game/repositories/SkillsRepository.cs.uid
new file mode 100644
index 0000000..bdb96d4
--- /dev/null
+++ b/game/repositories/SkillsRepository.cs.uid
@@ -0,0 +1 @@
+uid://cymx7mtmdblun
diff --git a/game/services/LevelService.cs b/game/services/LevelService.cs
new file mode 100644
index 0000000..4b14d2d
--- /dev/null
+++ b/game/services/LevelService.cs
@@ -0,0 +1,6 @@
+namespace Mr.BrickAdventures.game.services;
+
+public sealed class LevelService
+{
+
+}
\ No newline at end of file
diff --git a/game/services/LevelService.cs.uid b/game/services/LevelService.cs.uid
new file mode 100644
index 0000000..0eced1f
--- /dev/null
+++ b/game/services/LevelService.cs.uid
@@ -0,0 +1 @@
+uid://b2tsi3cjnh5xq
diff --git a/game/services/SaveService.cs b/game/services/SaveService.cs
new file mode 100644
index 0000000..b7b2722
--- /dev/null
+++ b/game/services/SaveService.cs
@@ -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();
+ }
+}
\ No newline at end of file
diff --git a/game/services/SaveService.cs.uid b/game/services/SaveService.cs.uid
new file mode 100644
index 0000000..aa8532c
--- /dev/null
+++ b/game/services/SaveService.cs.uid
@@ -0,0 +1 @@
+uid://dxqkxlkkivxog
diff --git a/game/services/SkillService.cs b/game/services/SkillService.cs
new file mode 100644
index 0000000..213a765
--- /dev/null
+++ b/game/services/SkillService.cs
@@ -0,0 +1,6 @@
+namespace Mr.BrickAdventures.game.services;
+
+public sealed class SkillService
+{
+
+}
\ No newline at end of file
diff --git a/game/services/SkillService.cs.uid b/game/services/SkillService.cs.uid
new file mode 100644
index 0000000..59bce5a
--- /dev/null
+++ b/game/services/SkillService.cs.uid
@@ -0,0 +1 @@
+uid://dim21hs12wdi5
diff --git a/objects/level/ui_layer.tscn b/objects/level/ui_layer.tscn
index 64549a3..219e5a3 100644
--- a/objects/level/ui_layer.tscn
+++ b/objects/level/ui_layer.tscn
@@ -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
diff --git a/objects/ui/pause_menu.tscn b/objects/ui/pause_menu.tscn
index d995ee9..14d62bf 100644
--- a/objects/ui/pause_menu.tscn
+++ b/objects/ui/pause_menu.tscn
@@ -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://cakgxndurgfa3" path="res://scripts/UI/PauseMenu.cs" id="1_aktha"]
[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", "MainMenuButton", "QuitButton", "SettingsButton")]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
@@ -16,12 +15,10 @@ 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")
+ResumeButton = NodePath("PanelContainer/MarginContainer/VBoxContainer/Resume Button")
+MainMenuButton = NodePath("PanelContainer/MarginContainer/VBoxContainer/Exit to menu Button")
+QuitButton = NodePath("PanelContainer/MarginContainer/VBoxContainer/Quit game Button")
+SettingsButton = NodePath("PanelContainer/MarginContainer/VBoxContainer/Settings Button")
[node name="PanelContainer" type="PanelContainer" parent="."]
layout_mode = 1
diff --git a/scenes/level_village_1.tscn b/scenes/level_village_1.tscn
index 73e8824..960c277 100644
--- a/scenes/level_village_1.tscn
+++ b/scenes/level_village_1.tscn
@@ -77,6 +77,10 @@ nodes_to_disable = [NodePath("../../Brick Player")]
skill_unlocker = NodePath("../../Brick Player/SkillUnlockerComponent")
components_to_disable = [NodePath("../../Brick Player")]
+[node name="Pause menu" parent="UI Layer" index="4" node_paths=PackedStringArray("PauseMenuControl", "SettingsControl")]
+PauseMenuControl = NodePath(".")
+SettingsControl = NodePath("../Settings menu")
+
[node name="Global Light" parent="." instance=ExtResource("4_mc58c")]
[node name="Camera2D" parent="." instance=ExtResource("5_sskgn")]
diff --git a/scenes/main_menu.tscn b/scenes/main_menu.tscn
index b1dac87..5f20dd9 100644
--- a/scenes/main_menu.tscn
+++ b/scenes/main_menu.tscn
@@ -1,27 +1,33 @@
-[gd_scene load_steps=6 format=3 uid="uid://cl00e2ocomk3m"]
+[gd_scene load_steps=8 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://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://cvfsbiy5ggrpg" path="res://objects/ui/input_settings.tscn" id="5_rtw2f"]
-[node name="Main menu" type="CanvasLayer"]
+[node name="AppRoot" type="Node2D"]
+script = ExtResource("1_rtw2f")
-[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")
+FirstLevelScene = ExtResource("3_oa1go")
+
+[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
diff --git a/scripts/UI/Hud.cs b/scripts/UI/Hud.cs
deleted file mode 100644
index 5939a8e..0000000
--- a/scripts/UI/Hud.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using Godot;
-using Mr.BrickAdventures.Autoloads;
-using Mr.BrickAdventures.scripts.components;
-
-namespace Mr.BrickAdventures.scripts.UI;
-
-public partial class Hud : Node
-{
- [Export] public HealthComponent Health { get; set; }
- [Export] public Label CoinsLabel { get; set; }
- [Export] public ProgressBar HealthBar { get; set; }
- [Export] public Label LivesLabel { get; set; }
-
- private GameManager _gameManager;
-
- public override void _Ready()
- {
- _gameManager = GetNode("/root/GameManager");
- }
-
- public override void _Process(double delta)
- {
- SetHealthBar();
- SetLivesLabel();
- SetCoinsLabel();
- }
-
- private void SetCoinsLabel()
- {
- CoinsLabel.Text = Tr("COINS_LABEL") + ": " + _gameManager.GetCoins();
- }
-
- private void SetLivesLabel()
- {
- LivesLabel.Text = Tr("LIVES_LABEL") + ": " + _gameManager.GetLives();
- }
-
- private void SetHealthBar()
- {
- HealthBar.Value = Health.Health;
- HealthBar.MaxValue = Health.MaxHealth;
- }
-}
\ No newline at end of file
diff --git a/scripts/UI/Hud.cs.uid b/scripts/UI/Hud.cs.uid
deleted file mode 100644
index 15ddc0b..0000000
--- a/scripts/UI/Hud.cs.uid
+++ /dev/null
@@ -1 +0,0 @@
-uid://wfj674u4486f