diff --git a/Scenes/Game.tscn b/Scenes/Game.tscn index 5081da7..6a0d4ac 100644 --- a/Scenes/Game.tscn +++ b/Scenes/Game.tscn @@ -19,6 +19,7 @@ [ext_resource type="Texture2D" uid="uid://dt84awx33mulb" path="res://Assets/Images/ResearchSymbol.png" id="13_alh3a"] [ext_resource type="Texture2D" uid="uid://bmcpkt6mae2qi" path="res://Assets/Images/AlarmSign.png" id="13_x3xnh"] [ext_resource type="Texture2D" uid="uid://uunnuou4g86w" path="res://Assets/Images/Resources/MushroomSymbol.png" id="14_food"] +[ext_resource type="Script" path="res://Scripts/UI/Tutorial/TutorialBubble.cs" id="15_tutorial"] [sub_resource type="CompressedTexture2D" id="CompressedTexture2D_u44n3"] @@ -111,6 +112,7 @@ grow_vertical = 2 [node name="HeaderContainer" type="PanelContainer" parent="CanvasLayer/UIHandler/MainUI" unique_id=1744492333] layout_mode = 2 +custom_minimum_size = Vector2(0, 48) theme_override_styles/panel = SubResource("StyleBoxFlat_b2bpf") [node name="VBoxContainer" type="HBoxContainer" parent="CanvasLayer/UIHandler/MainUI/HeaderContainer" unique_id=536619770] @@ -179,7 +181,7 @@ vertical_alignment = 1 [node name="CurrentLayer" type="RichTextLabel" parent="CanvasLayer/UIHandler/MainUI/HeaderContainer/VBoxContainer" unique_id=1435047910] layout_mode = 2 -text = "Current layer: XXX" +text = "Layer: 1/10" fit_content = true autowrap_mode = 0 horizontal_alignment = 1 @@ -187,7 +189,7 @@ vertical_alignment = 1 [node name="DeepestLayer" type="RichTextLabel" parent="CanvasLayer/UIHandler/MainUI/HeaderContainer/VBoxContainer" unique_id=600512938] layout_mode = 2 -text = "Deepest layer: XXX" +text = "Gate depth: 0" fit_content = true autowrap_mode = 0 horizontal_alignment = 1 @@ -195,7 +197,8 @@ vertical_alignment = 1 [node name="UnlockLayer" type="Button" parent="CanvasLayer/UIHandler/MainUI/HeaderContainer/VBoxContainer" unique_id=1538511285] layout_mode = 2 -text = "Unlock layer" +tooltip_text = "Open the next gate when all requirements are available" +text = "Open Gate" [node name="Content" type="Control" parent="CanvasLayer/UIHandler/MainUI" unique_id=45665557] layout_mode = 2 @@ -226,7 +229,7 @@ theme_override_constants/separation = 20 [node name="Title" type="RichTextLabel" parent="CanvasLayer/UIHandler/MainUI/Content/CodingWindow/VBoxContainer" unique_id=90213926] layout_mode = 2 bbcode_enabled = true -text = "[font_size=32]Script editor[/font_size]" +text = "[font_size=32]Robot Script[/font_size]" fit_content = true autowrap_mode = 0 horizontal_alignment = 1 @@ -238,7 +241,7 @@ theme_override_constants/separation = 20 [node name="RichTextLabel" type="RichTextLabel" parent="CanvasLayer/UIHandler/MainUI/Content/CodingWindow/VBoxContainer/Renaming" unique_id=1964282829] layout_mode = 2 size_flags_horizontal = 3 -text = "Rename your robot:" +text = "Robot name" fit_content = true autowrap_mode = 0 horizontal_alignment = 1 @@ -253,7 +256,7 @@ max_length = 24 [node name="Button" type="Button" parent="CanvasLayer/UIHandler/MainUI/Content/CodingWindow/VBoxContainer/Renaming" unique_id=279251707] layout_mode = 2 size_flags_horizontal = 3 -text = "Submit" +text = "Apply" [node name="Scripting" type="HBoxContainer" parent="CanvasLayer/UIHandler/MainUI/Content/CodingWindow/VBoxContainer" unique_id=1934932205] layout_mode = 2 @@ -348,7 +351,7 @@ layout_mode = 2 [node name="Title" type="RichTextLabel" parent="CanvasLayer/UIHandler/MainUI/Content/RobotList/VBoxContainer" unique_id=1891991275] layout_mode = 2 bbcode_enabled = true -text = "[font_size=32]Robotlist[/font_size]" +text = "[font_size=32]Robots[/font_size]" fit_content = true autowrap_mode = 0 horizontal_alignment = 1 @@ -409,15 +412,16 @@ anchor_left = 0.5 anchor_top = 0.5 anchor_right = 0.5 anchor_bottom = 0.5 -offset_left = -20.0 -offset_top = -20.0 -offset_right = 20.0 -offset_bottom = 20.0 +offset_left = -120.0 +offset_top = -125.0 +offset_right = 120.0 +offset_bottom = 125.0 grow_horizontal = 2 grow_vertical = 2 [node name="VBoxContainer" type="VBoxContainer" parent="CanvasLayer/UIHandler/MainUI/Content/Menu" unique_id=1924672355] layout_mode = 2 +theme_override_constants/separation = 10 [node name="Button" type="Button" parent="CanvasLayer/UIHandler/MainUI/Content/Menu/VBoxContainer" unique_id=1938871792] layout_mode = 2 @@ -470,7 +474,7 @@ horizontal_alignment = 1 [node name="RichTextLabel" type="RichTextLabel" parent="CanvasLayer/UIHandler/MainUI/Content/Inventory/VBoxContainer" unique_id=1572448693] layout_mode = 2 -text = "Available Space:" +text = "Storage" fit_content = true autowrap_mode = 0 horizontal_alignment = 1 @@ -551,6 +555,7 @@ text = "Continue" [node name="FooterContainer" type="PanelContainer" parent="CanvasLayer/UIHandler/MainUI" unique_id=1495029884] layout_mode = 2 +custom_minimum_size = Vector2(0, 48) [node name="HBoxContainer" type="HBoxContainer" parent="CanvasLayer/UIHandler/MainUI/FooterContainer" unique_id=1782436702] layout_mode = 2 @@ -597,6 +602,71 @@ layout_mode = 2 tooltip_text = "Menu (ESC)" texture_normal = ExtResource("12_3so38") +[node name="TutorialBubble" type="PanelContainer" parent="CanvasLayer/UIHandler" node_paths=PackedStringArray("speakerLabel", "contentLabel", "progressLabel", "nextButton", "skipButton")] +layout_mode = 1 +anchors_preset = 2 +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_left = 24.0 +offset_top = -236.0 +offset_right = 544.0 +offset_bottom = -72.0 +grow_vertical = 0 +script = ExtResource("15_tutorial") +speakerLabel = NodePath("MarginContainer/VBoxContainer/Header/Speaker") +contentLabel = NodePath("MarginContainer/VBoxContainer/Content") +progressLabel = NodePath("MarginContainer/VBoxContainer/Footer/Progress") +nextButton = NodePath("MarginContainer/VBoxContainer/Footer/Next") +skipButton = NodePath("MarginContainer/VBoxContainer/Footer/Skip") + +[node name="MarginContainer" type="MarginContainer" parent="CanvasLayer/UIHandler/TutorialBubble"] +layout_mode = 2 +theme_override_constants/margin_left = 14 +theme_override_constants/margin_top = 12 +theme_override_constants/margin_right = 14 +theme_override_constants/margin_bottom = 12 + +[node name="VBoxContainer" type="VBoxContainer" parent="CanvasLayer/UIHandler/TutorialBubble/MarginContainer"] +layout_mode = 2 +theme_override_constants/separation = 8 + +[node name="Header" type="HBoxContainer" parent="CanvasLayer/UIHandler/TutorialBubble/MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Speaker" type="RichTextLabel" parent="CanvasLayer/UIHandler/TutorialBubble/MarginContainer/VBoxContainer/Header"] +layout_mode = 2 +size_flags_horizontal = 3 +bbcode_enabled = true +text = "[font_size=22]B.O.B.[/font_size]" +fit_content = true +autowrap_mode = 0 + +[node name="Content" type="RichTextLabel" parent="CanvasLayer/UIHandler/TutorialBubble/MarginContainer/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 +text = "Tutorial" +fit_content = true +autowrap_mode = 2 + +[node name="Footer" type="HBoxContainer" parent="CanvasLayer/UIHandler/TutorialBubble/MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_override_constants/separation = 10 + +[node name="Progress" type="RichTextLabel" parent="CanvasLayer/UIHandler/TutorialBubble/MarginContainer/VBoxContainer/Footer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "1/1" +fit_content = true +autowrap_mode = 0 + +[node name="Skip" type="Button" parent="CanvasLayer/UIHandler/TutorialBubble/MarginContainer/VBoxContainer/Footer"] +layout_mode = 2 +text = "Skip" + +[node name="Next" type="Button" parent="CanvasLayer/UIHandler/TutorialBubble/MarginContainer/VBoxContainer/Footer"] +layout_mode = 2 +text = "Next" + [connection signal="pressed" from="CanvasLayer/UIHandler/MainUI/HeaderContainer/VBoxContainer/UnlockLayer" to="CanvasLayer/UIHandler" method="UnlockLayer"] [connection signal="pressed" from="CanvasLayer/UIHandler/MainUI/Content/CodingWindow/VBoxContainer/Renaming/Button" to="CanvasLayer/UIHandler/MainUI/Content/CodingWindow" method="SaveRobotName"] [connection signal="button_up" from="CanvasLayer/UIHandler/MainUI/Content/CodingWindow/VBoxContainer/Scripting/EditorWindow/Buttons/Clear" to="CanvasLayer/UIHandler/MainUI/Content/CodingWindow" method="ClearWindow"] diff --git a/Scenes/MainMenu.tscn b/Scenes/MainMenu.tscn index e876fd4..59ca8a6 100644 --- a/Scenes/MainMenu.tscn +++ b/Scenes/MainMenu.tscn @@ -108,7 +108,7 @@ theme_override_constants/separation = 40 [node name="RichTextLabel" type="RichTextLabel" parent="CenterContainer/VBoxContainer" unique_id=195514567] layout_mode = 2 bbcode_enabled = true -text = "[font_size=50]RuinAdventurer[/font_size]" +text = "[font_size=50]Ruin Adventurer[/font_size]" fit_content = true scroll_active = false autowrap_mode = 0 @@ -119,24 +119,28 @@ layout_mode = 2 [node name="btnPlay" type="Button" parent="CenterContainer/VBoxContainer" unique_id=452402808] layout_mode = 2 +custom_minimum_size = Vector2(220, 42) theme_override_styles/normal = SubResource("StyleBoxFlat_bnhvo") theme_override_styles/hover = SubResource("StyleBoxFlat_tt5f1") text = "Start Game" [node name="btnLoad" type="Button" parent="CenterContainer/VBoxContainer"] layout_mode = 2 +custom_minimum_size = Vector2(220, 42) theme_override_styles/normal = SubResource("StyleBoxFlat_bnhvo") theme_override_styles/hover = SubResource("StyleBoxFlat_tt5f1") text = "Load Game" [node name="btnOptions" type="Button" parent="CenterContainer/VBoxContainer" unique_id=891656915] layout_mode = 2 +custom_minimum_size = Vector2(220, 42) theme_override_styles/normal = SubResource("StyleBoxFlat_bnhvo") theme_override_styles/hover = SubResource("StyleBoxFlat_tt5f1") text = "Options" [node name="btnExit" type="Button" parent="CenterContainer/VBoxContainer" unique_id=2025231658] layout_mode = 2 +custom_minimum_size = Vector2(220, 42) theme_override_styles/normal = SubResource("StyleBoxFlat_bnhvo") theme_override_styles/hover = SubResource("StyleBoxFlat_tt5f1") text = "Exit Game" diff --git a/Scenes/WorldSetup.tscn b/Scenes/WorldSetup.tscn new file mode 100644 index 0000000..11d64d3 --- /dev/null +++ b/Scenes/WorldSetup.tscn @@ -0,0 +1,75 @@ +[gd_scene format=3] + +[ext_resource type="Script" path="res://Scripts/UI/Menus/WorldSetup.cs" id="1_world_setup"] + +[node name="WorldSetup" type="Control" node_paths=PackedStringArray("seedInput", "seedPreview")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_world_setup") +seedInput = NodePath("CenterContainer/PanelContainer/VBoxContainer/SeedInput") +seedPreview = NodePath("CenterContainer/PanelContainer/VBoxContainer/SeedPreview") + +[node name="CenterContainer" type="CenterContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="PanelContainer" type="PanelContainer" parent="CenterContainer"] +layout_mode = 2 +custom_minimum_size = Vector2(440, 260) + +[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer/PanelContainer"] +layout_mode = 2 +theme_override_constants/separation = 14 + +[node name="Title" type="RichTextLabel" parent="CenterContainer/PanelContainer/VBoxContainer"] +layout_mode = 2 +bbcode_enabled = true +text = "[font_size=34]World Setup[/font_size]" +fit_content = true +autowrap_mode = 0 +horizontal_alignment = 1 + +[node name="Description" type="RichTextLabel" parent="CenterContainer/PanelContainer/VBoxContainer"] +layout_mode = 2 +text = "Choose a seed for the ruin layout. Leave it empty for a random expedition." +fit_content = true +autowrap_mode = 2 +horizontal_alignment = 1 + +[node name="SeedInput" type="LineEdit" parent="CenterContainer/PanelContainer/VBoxContainer"] +layout_mode = 2 +placeholder_text = "Seed..." +max_length = 32 + +[node name="SeedPreview" type="RichTextLabel" parent="CenterContainer/PanelContainer/VBoxContainer"] +layout_mode = 2 +text = "No seed entered. B.O.B. will pick one from the ruins." +fit_content = true +autowrap_mode = 2 +horizontal_alignment = 1 + +[node name="Buttons" type="HBoxContainer" parent="CenterContainer/PanelContainer/VBoxContainer"] +layout_mode = 2 +theme_override_constants/separation = 12 + +[node name="Back" type="Button" parent="CenterContainer/PanelContainer/VBoxContainer/Buttons"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Back" + +[node name="Start" type="Button" parent="CenterContainer/PanelContainer/VBoxContainer/Buttons"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Enter Ruins" + +[connection signal="text_changed" from="CenterContainer/PanelContainer/VBoxContainer/SeedInput" to="." method="OnSeedChanged"] +[connection signal="pressed" from="CenterContainer/PanelContainer/VBoxContainer/Buttons/Back" to="." method="OnBackPressed"] +[connection signal="pressed" from="CenterContainer/PanelContainer/VBoxContainer/Buttons/Start" to="." method="OnStartPressed"] diff --git a/Scripts/Core/GameData.cs b/Scripts/Core/GameData.cs index f48420e..84eec0f 100644 --- a/Scripts/Core/GameData.cs +++ b/Scripts/Core/GameData.cs @@ -4,7 +4,7 @@ using Godot; public partial class GameData { - public static bool debugMode = true; + public static bool debugMode = false; public static Random rand = new Random(seed); public static Layer[] map; @@ -24,6 +24,7 @@ public partial class GameData public static RobotStats robotStats = new RobotStats(); public static Dictionary> gateUnlocks; public static bool loadSaveOnStart = false; + public static bool showTutorial = true; public static Color primaryColor = new Color("#276ac2"); public static Color lightColor = new Color("#7efff5"); @@ -36,7 +37,6 @@ public partial class GameData public static void ResetRunState() { - seed = 12345; ruinSize = 10; layerSize = 20; rand = new Random(seed); diff --git a/Scripts/UI/Common/UIHandler.cs b/Scripts/UI/Common/UIHandler.cs index 726f1df..6595c4a 100644 --- a/Scripts/UI/Common/UIHandler.cs +++ b/Scripts/UI/Common/UIHandler.cs @@ -29,6 +29,7 @@ public partial class UIHandler : Control private bool receivedRobotJumpSignal = false; public override void _Ready() { + UIStyle.Apply(this); robotList.OnRobotJumpTo += OnRobotJumpTo; } @@ -177,6 +178,10 @@ public partial class UIHandler : Control waterLabel.Text = $"Water: {GameData.survival.thirst:0}/{GameData.survival.maxThirst:0}"; hungerLabel.Text = $"Food: {GameData.survival.hunger:0}/{GameData.survival.maxHunger:0}"; survivalStatus.Text = GameData.survival.currentStatus; + survivalStatus.Modulate = GameData.survival.currentStatus.Contains("critical") + ? UIStyle.GetWarningColor() + : Colors.White; + if (GameData.survival.isDead) { ShowGameOver(); @@ -185,13 +190,14 @@ public partial class UIHandler : Control private void DisplayWorldStats() { - currentLayer.Text = $"Current layer: {GameData.currentLayer}/{GameData.ruinSize}"; - deepestLayer.Text = $"Deepest layer: {GameData.lowestLayer}"; - if(GameData.lowestLayer == GameData.ruinSize){ + currentLayer.Text = $"Layer: {GameData.currentLayer + 1}/{GameData.ruinSize}"; + deepestLayer.Text = $"Gate depth: {GameData.lowestLayer}"; + if (GameData.lowestLayer == GameData.ruinSize) + { unlockLayer.Visible = false; return; } - unlockLayer.TooltipText = "Needed items: \r" + GameData.map[GameData.lowestLayer].DisplayGateIngredients(); + unlockLayer.TooltipText = "Gate requirements:\r" + GameData.map[GameData.lowestLayer].DisplayGateIngredients(); unlockLayer.Disabled = !GameData.inventory.CanCraft(GameData.map[GameData.lowestLayer].gateIngredients, 1); } @@ -213,8 +219,8 @@ public partial class UIHandler : Control public void ShowGameOver() { - if(gameOver.Visible) return; - gameOver.GetNode("./VBoxContainer/Content").Text = $"[font_size=32]You died! \r Reason: {GameData.survival.deathReason} \r Better luck next time \r"; + if (gameOver.Visible) return; + gameOver.GetNode("./VBoxContainer/Content").Text = $"[font_size=32]You died!\rReason: {GameData.survival.deathReason}\rBetter luck next time.\r"; gameOver.Show(); } diff --git a/Scripts/UI/Common/UIStyle.cs b/Scripts/UI/Common/UIStyle.cs new file mode 100644 index 0000000..30709db --- /dev/null +++ b/Scripts/UI/Common/UIStyle.cs @@ -0,0 +1,157 @@ +using Godot; + +public static class UIStyle +{ + private static readonly Color Background = new Color("#101317"); + private static readonly Color Surface = new Color("#171d22"); + private static readonly Color SurfaceStrong = new Color("#202a31"); + private static readonly Color SurfaceSoft = new Color("#25343d"); + private static readonly Color Accent = new Color("#6ec6b8"); + private static readonly Color AccentStrong = new Color("#9be7d8"); + private static readonly Color Warning = new Color("#d69b5a"); + private static readonly Color Text = new Color("#e8f0ed"); + private static readonly Color MutedText = new Color("#aab8b4"); + + public static void Apply(Control root) + { + if (root == null) return; + + ApplyToControl(root); + + foreach (Node child in root.GetChildren()) + { + Control control = child as Control; + if (control != null) + { + Apply(control); + } + } + } + + private static void ApplyToControl(Control control) + { + PanelContainer panel = control as PanelContainer; + if (panel != null) + { + StylePanel(panel); + } + + Button button = control as Button; + if (button != null) + { + StyleButton(button); + } + + LineEdit lineEdit = control as LineEdit; + if (lineEdit != null) + { + StyleLineEdit(lineEdit); + } + + OptionButton optionButton = control as OptionButton; + if (optionButton != null) + { + StyleButton(optionButton); + } + + RichTextLabel label = control as RichTextLabel; + if (label != null) + { + StyleLabel(label); + } + + TextureButton textureButton = control as TextureButton; + if (textureButton != null) + { + StyleTextureButton(textureButton); + } + + TextureRect textureRect = control as TextureRect; + if (textureRect != null) + { + textureRect.CustomMinimumSize = new Vector2(26, 26); + } + } + + private static void StylePanel(PanelContainer panel) + { + Color color = Surface; + + if (panel.Name.ToString().Contains("Header") || panel.Name.ToString().Contains("Footer")) + { + color = Background; + } + + if (panel.Name.ToString().Contains("GameOver") || panel.Name.ToString().Contains("Menu")) + { + color = SurfaceStrong; + } + + StyleBoxFlat style = CreateBox(color, Accent, 1, 8); + panel.AddThemeStyleboxOverride("panel", style); + } + + private static void StyleButton(Button button) + { + button.CustomMinimumSize = new Vector2(112, 34); + button.AddThemeStyleboxOverride("normal", CreateBox(SurfaceSoft, Accent, 1, 6)); + button.AddThemeStyleboxOverride("hover", CreateBox(new Color("#314750"), AccentStrong, 1, 6)); + button.AddThemeStyleboxOverride("pressed", CreateBox(new Color("#1a2b30"), AccentStrong, 1, 6)); + button.AddThemeStyleboxOverride("focus", CreateBox(new Color(0, 0, 0, 0), AccentStrong, 1, 6)); + button.AddThemeColorOverride("font_color", Text); + button.AddThemeColorOverride("font_hover_color", AccentStrong); + button.AddThemeColorOverride("font_pressed_color", AccentStrong); + button.AddThemeColorOverride("font_disabled_color", MutedText); + } + + private static void StyleLineEdit(LineEdit lineEdit) + { + lineEdit.CustomMinimumSize = new Vector2(120, 34); + lineEdit.AddThemeStyleboxOverride("normal", CreateBox(new Color("#11191d"), Accent, 1, 6)); + lineEdit.AddThemeStyleboxOverride("focus", CreateBox(new Color("#142126"), AccentStrong, 1, 6)); + lineEdit.AddThemeColorOverride("font_color", Text); + lineEdit.AddThemeColorOverride("font_placeholder_color", MutedText); + } + + private static void StyleLabel(RichTextLabel label) + { + label.AddThemeColorOverride("default_color", Text); + label.AddThemeColorOverride("font_shadow_color", new Color("#00000080")); + } + + private static void StyleTextureButton(TextureButton textureButton) + { + textureButton.CustomMinimumSize = new Vector2(38, 38); + textureButton.Modulate = Text; + textureButton.SelfModulate = Text; + textureButton.StretchMode = TextureButton.StretchModeEnum.KeepAspectCentered; + } + + private static StyleBoxFlat CreateBox(Color color, Color borderColor, int borderWidth, int radius) + { + StyleBoxFlat style = new StyleBoxFlat + { + BgColor = color, + BorderColor = borderColor, + BorderWidthTop = borderWidth, + BorderWidthBottom = borderWidth, + BorderWidthLeft = borderWidth, + BorderWidthRight = borderWidth, + CornerRadiusTopLeft = radius, + CornerRadiusTopRight = radius, + CornerRadiusBottomLeft = radius, + CornerRadiusBottomRight = radius, + ContentMarginLeft = 8, + ContentMarginRight = 8, + ContentMarginTop = 6, + ContentMarginBottom = 6 + }; + + return style; + } + + public static Color GetWarningColor() + { + return Warning; + } +} diff --git a/Scripts/UI/Common/UIStyle.cs.uid b/Scripts/UI/Common/UIStyle.cs.uid new file mode 100644 index 0000000..4560ab9 --- /dev/null +++ b/Scripts/UI/Common/UIStyle.cs.uid @@ -0,0 +1 @@ +uid://dkygddm6k0hjw diff --git a/Scripts/UI/Menus/MainMenu.cs b/Scripts/UI/Menus/MainMenu.cs index b6f3c3a..599df98 100644 --- a/Scripts/UI/Menus/MainMenu.cs +++ b/Scripts/UI/Menus/MainMenu.cs @@ -2,10 +2,15 @@ using Godot; public partial class MainMenu : Control { + public override void _Ready() + { + UIStyle.Apply(this); + } + public void OnPlayPressed() { GameData.loadSaveOnStart = false; - GetTree().ChangeSceneToFile("res://Scenes/Game.tscn"); + GetTree().ChangeSceneToFile("res://Scenes/WorldSetup.tscn"); } public void OnLoadPressed() @@ -13,6 +18,7 @@ public partial class MainMenu : Control if (!SaveGameManager.SaveExists()) return; GameData.loadSaveOnStart = true; + GameData.showTutorial = false; GetTree().ChangeSceneToFile("res://Scenes/Game.tscn"); } diff --git a/Scripts/UI/Menus/WorldSetup.cs b/Scripts/UI/Menus/WorldSetup.cs new file mode 100644 index 0000000..a25d625 --- /dev/null +++ b/Scripts/UI/Menus/WorldSetup.cs @@ -0,0 +1,72 @@ +using Godot; +using System; + +public partial class WorldSetup : Control +{ + [Export] LineEdit seedInput; + [Export] RichTextLabel seedPreview; + + public override void _Ready() + { + UIStyle.Apply(this); + UpdateSeedPreview(); + } + + public void OnSeedChanged(string text) + { + UpdateSeedPreview(); + } + + public void OnStartPressed() + { + GameData.seed = GetSelectedSeed(); + GameData.loadSaveOnStart = false; + GameData.showTutorial = true; + GetTree().ChangeSceneToFile("res://Scenes/Game.tscn"); + } + + public void OnBackPressed() + { + GetTree().ChangeSceneToFile("res://Scenes/MainMenu.tscn"); + } + + private int GetSelectedSeed() + { + string text = seedInput.Text.Trim(); + if (text.Length <= 0) + { + return new Random().Next(1, int.MaxValue); + } + + int numericSeed; + if (int.TryParse(text, out numericSeed)) + { + return numericSeed; + } + + return CreateTextSeed(text); + } + + private int CreateTextSeed(string text) + { + int hash = 17; + foreach (char character in text) + { + hash = hash * 31 + character; + } + + return Math.Abs(hash); + } + + private void UpdateSeedPreview() + { + string text = seedInput.Text.Trim(); + if (text.Length <= 0) + { + seedPreview.Text = "No seed entered. B.O.B. will pick one from the ruins."; + return; + } + + seedPreview.Text = "Selected seed: " + GetSelectedSeed(); + } +} diff --git a/Scripts/UI/Menus/WorldSetup.cs.uid b/Scripts/UI/Menus/WorldSetup.cs.uid new file mode 100644 index 0000000..baa54aa --- /dev/null +++ b/Scripts/UI/Menus/WorldSetup.cs.uid @@ -0,0 +1 @@ +uid://3xjyrxtq3rww diff --git a/Scripts/UI/Tutorial/TutorialBubble.cs b/Scripts/UI/Tutorial/TutorialBubble.cs new file mode 100644 index 0000000..b93b103 --- /dev/null +++ b/Scripts/UI/Tutorial/TutorialBubble.cs @@ -0,0 +1,78 @@ +using Godot; +using System.Collections.Generic; + +public partial class TutorialBubble : PanelContainer +{ + [Export] RichTextLabel speakerLabel; + [Export] RichTextLabel contentLabel; + [Export] RichTextLabel progressLabel; + [Export] Button nextButton; + [Export] Button skipButton; + + private List messages = new List(); + private int currentIndex = 0; + + public override void _Ready() + { + UIStyle.Apply(this); + CreateMessages(); + + nextButton.Pressed += ShowNext; + skipButton.Pressed += SkipTutorial; + + if (!GameData.showTutorial) + { + Hide(); + return; + } + + ShowMessage(0); + } + + private void CreateMessages() + { + messages = new List + { + "Welcome to the ruin. I am B.O.B.", + "B.O.B. means Building Operation Buddy. I will keep the lamps on while you teach the robots what to do.", + "The last remaining unit in this ruin saved you from your fall and brought you here. If you want to leave, you have to explore the ruin", + "You do not walk through the ruin yourself. Your robots explore, harvest, craft and carry progress for you.", + "The top bar shows survival pressure: energy, water and food. If those run out, the expedition ends.", + "Open the robot panel (Default: [R]) to inspect your robots. A robot can overheat, lose maintenance and slow down if ignored.", + "Use the script editor (Clicking 'Jump to' from the robot panel) to give robots commands. Start simple: move, explore, harvest, then craft.", + "Research unlocks better tools, buildings, robot upgrades and deeper progression. The graph is your technology map (Default: [T]).", + "The inventory (Default: [I]) stores everything your robots collect. Gates and research both consume items from it.", + "Each gate blocks the next layer. When you have the required items, use Open Gate in the top bar.", + "The map (Default: [M]) shows what your robots have discovered. Exploration matters because resources are hidden in the ruin.", + "That is enough briefing. Build a loop, keep the robots alive, and open the lower gates. B.O.B. believes in organized chaos." + }; + } + + private void ShowNext() + { + currentIndex++; + if (currentIndex >= messages.Count) + { + SkipTutorial(); + return; + } + + ShowMessage(currentIndex); + } + + private void SkipTutorial() + { + GameData.showTutorial = false; + Hide(); + } + + private void ShowMessage(int index) + { + currentIndex = index; + speakerLabel.Text = "B.O.B."; + contentLabel.Text = messages[index]; + progressLabel.Text = $"{index + 1}/{messages.Count}"; + nextButton.Text = index == messages.Count - 1 ? "Done" : "Next"; + Show(); + } +} diff --git a/Scripts/UI/Tutorial/TutorialBubble.cs.uid b/Scripts/UI/Tutorial/TutorialBubble.cs.uid new file mode 100644 index 0000000..a2391bd --- /dev/null +++ b/Scripts/UI/Tutorial/TutorialBubble.cs.uid @@ -0,0 +1 @@ +uid://d0opysuqksr6l