Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 104 additions & 4 deletions Assets/_Project/Scripts/Presentation/UI/PauseMenuHud.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ namespace Nova.Presentation.UI
/// <para>
/// READ-ONLY toward the simulation: the only state this component
/// touches is the kernel clock (pause/resume); it never submits a
/// command. Settings are deliberately NOT a second mask here — they live
/// in the main menu, and the panel says so instead of drifting a copy.
/// command. The settings rows render the shared GameSettingsStore in
/// place (owner request after the T-03 round): same gateway, same
/// persistence as the main-menu mask — not a copy of state.
/// </para>
/// </summary>
[DisallowMultipleComponent]
Expand All @@ -61,6 +62,7 @@ public sealed class PauseMenuHud : MonoBehaviour

private bool _menuOpen;
private bool _pausedByMenu;
private bool _settingsOpen;

private GUIStyle _headlineStyle;
private GUIStyle _bodyStyle;
Expand All @@ -86,6 +88,7 @@ private void OnDisable()
// so a re-enabled component (next match start) starts clean.
_menuOpen = false;
_pausedByMenu = false;
_settingsOpen = false;
ModalSurfaceLink.Reset();
}

Expand Down Expand Up @@ -184,7 +187,7 @@ private void OnGUI()
Matrix4x4 previousMatrix = GUI.matrix;
GUI.matrix = Matrix4x4.Scale(new Vector3(scale, scale, 1f));

Rect rect = CenteredRect(250f);
Rect rect = CenteredRect(_settingsOpen ? 470f : 250f);
GUI.Box(rect, GUIContent.none, HudChrome.OpaquePanelStyle);
GUILayout.BeginArea(rect);
GUILayout.Space(HudChrome.OpaquePanelStyle.padding.top);
Expand All @@ -199,6 +202,15 @@ private void OnGUI()
{
CloseMenu(playClick: true);
}
if (GUILayout.Button(_settingsOpen ? "Einstellungen ausblenden" : "Einstellungen …", _buttonStyle, GUILayout.Height(28f)))
{
AudioServiceLocator.Play2D(SoundEventId.UI_Click);
_settingsOpen = !_settingsOpen;
}
if (_settingsOpen)
{
DrawSettingsRows();
}
if (GUILayout.Button("Zum Hauptmenü", _buttonStyle, GUILayout.Height(30f)))
{
AudioServiceLocator.Play2D(SoundEventId.UI_Click);
Expand All @@ -214,7 +226,6 @@ private void OnGUI()
if (_menu != null) _menu.Quit();
}
GUILayout.Space(6f);
GUILayout.Label("Einstellungen (Ton, Bild, Auflösung) gibt es im Hauptmenü.", _bodyStyle);
GUILayout.EndArea();

GUI.matrix = previousMatrix;
Expand All @@ -228,6 +239,95 @@ private Rect CenteredRect(float height)
return new Rect(x, y, _panelWidth, height);
}

/// <summary>
/// The in-menu settings rows (owner request, T-03 feedback round):
/// the same functions the main menu's settings mask owns — music and
/// SFX on/off + volume, fullscreen, resolution — writing through the
/// same GameSettingsStore gateway, so a change applies at once and
/// persists. A second mask would drift; these rows are the pause
/// menu's own rendering of the SAME store, not a copy of state.
/// </summary>
private void DrawSettingsRows()
{
GameSettings settings = GameSettingsStore.Current;

bool musicEnabled = GUILayout.Toggle(settings.musicEnabled, " Musik an", _bodyStyle);
float musicVolume = DrawVolumeSlider("Musik", settings.musicVolume);
bool sfxEnabled = GUILayout.Toggle(settings.sfxEnabled, " SFX an", _bodyStyle);
float sfxVolume = DrawVolumeSlider("SFX", settings.sfxVolume);
bool fullScreen = GUILayout.Toggle(settings.fullScreen, " Vollbild", _bodyStyle);
bool cycleResolution = GUILayout.Button(ResolutionLabel(settings), _buttonStyle, GUILayout.Height(24f));

if (musicEnabled != settings.musicEnabled
|| sfxEnabled != settings.sfxEnabled
|| fullScreen != settings.fullScreen
|| !Mathf.Approximately(musicVolume, settings.musicVolume)
|| !Mathf.Approximately(sfxVolume, settings.sfxVolume))
{
settings.musicEnabled = musicEnabled;
settings.musicVolume = musicVolume;
settings.sfxEnabled = sfxEnabled;
settings.sfxVolume = sfxVolume;
settings.fullScreen = fullScreen;
GameSettingsStore.ApplyAndSave();
}

if (cycleResolution)
{
CycleResolution(settings);
}
}

/// <summary>One labelled volume slider row ("Musik — 40 %").</summary>
private float DrawVolumeSlider(string label, float value)
{
GUILayout.BeginHorizontal();
GUILayout.Label($"{label} — {Mathf.RoundToInt(value * 100f)} %", _bodyStyle, GUILayout.Width(110f));
float next = GUILayout.HorizontalSlider(value, 0f, 1f);
GUILayout.EndHorizontal();
return next;
}

private static string ResolutionLabel(GameSettings settings)
{
return settings.resolutionWidth > 0
? $"Auflösung: {settings.resolutionWidth}×{settings.resolutionHeight}"
: $"Auflösung: aktuell ({Screen.width}×{Screen.height})";
}

/// <summary>Cycles the stored resolution through the display's supported modes (next on each click, wrapping).</summary>
private static void CycleResolution(GameSettings settings)
{
Resolution[] modes = Screen.resolutions;
if (modes.Length == 0) return;

int index = -1;
for (int i = 0; i < modes.Length; i++)
{
if (modes[i].width == settings.resolutionWidth && modes[i].height == settings.resolutionHeight)
{
index = i;
break;
}
}
// No stored match yet (0×0 = native): start from the CURRENT mode.
if (index < 0)
{
for (int i = 0; i < modes.Length; i++)
{
if (modes[i].width == Screen.width && modes[i].height == Screen.height)
{
index = i;
break;
}
}
}
Resolution next = modes[(index + 1) % modes.Length];
settings.resolutionWidth = next.width;
settings.resolutionHeight = next.height;
GameSettingsStore.ApplyAndSave();
}

private void EnsureStyles()
{
if (_headlineStyle == null)
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ die Versionierung folgt (in der aktuellen Doku-Phase) dem Dokumentationsstand de
eigene Bystander-Zeile ohne Befehlsbeitrag) und trägt bei einer
beschädigten Einzelauswahl deren HP im Titel; rein sim-lesend, ohne
Eingriff in die Simulation
- **Einstellungen jetzt auch im Pausemenü.** Auf Inhaber-Wunsch nach der
T-03-Proberunde: das Pausemenü enthält dieselben Funktionen wie die
Hauptmenü-Maske — Musik und SFX jeweils mit An/Aus und Lautstärke, Vollbild
und Auflösungswechsel. Beide Oberflächen schreiben über dasselbe Gateway
(`GameSettingsStore.ApplyAndSave`): wirkt sofort, persistiert in dieselbe
`settings.json`, kein Zustands-Duplikat
- **Restbestand der Vorkommen anklickbar und sichtbar (Paket 21.2, #86).**
Ein Linksklick auf ein Aetherium-Vorkommen (auch ein erschöpftes) zeigt in der
Befehlskarte Restbestand und Anfangsreserve („Aetherium-Vorkommen — 6.420 /
Expand Down
Loading