Added random seed and ability to enter a seed. Also added a simple/small tutorial screen and unified the UI with UIStyle.cs
This commit is contained in:
@@ -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<RichTextLabel>("./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<RichTextLabel>("./VBoxContainer/Content").Text = $"[font_size=32]You died!\rReason: {GameData.survival.deathReason}\rBetter luck next time.\r";
|
||||
gameOver.Show();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://dkygddm6k0hjw
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://3xjyrxtq3rww
|
||||
@@ -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<string> messages = new List<string>();
|
||||
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<string>
|
||||
{
|
||||
"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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://d0opysuqksr6l
|
||||
Reference in New Issue
Block a user