From b9ef0baf16b65243b78a845f77f97440c70176bb Mon Sep 17 00:00:00 2001 From: Michal Vrba Date: Sat, 29 Aug 2026 09:36:52 +0200 Subject: [PATCH] imperopvements --- src/NullDepth/Game/GameService.cs | 40 ++++ src/NullDepth/Models/GameModels.cs | 9 + src/NullDepth/Pages/Home.razor | 197 +++++++++++++++++- .../wwwroot/assets/audio/ai_architect_01.wav | Bin 0 -> 194084 bytes .../wwwroot/assets/audio/ai_architect_02.wav | Bin 0 -> 211724 bytes .../wwwroot/assets/audio/ai_architect_03.wav | Bin 0 -> 220544 bytes .../wwwroot/assets/audio/ai_architect_04.wav | Bin 0 -> 185264 bytes .../assets/audio/sfx_puzzle_success.wav | Bin 0 -> 70604 bytes .../images/endings/ending_containment.jpg | Bin 0 -> 841637 bytes .../assets/images/endings/ending_release.jpg | Bin 0 -> 936517 bytes .../images/endings/ending_shutdown.webp | Bin 0 -> 41560 bytes .../locations/loc_01_exterior_access_open.jpg | Bin 0 -> 568886 bytes .../loc_09_cooling_junction_cryo.jpg | Bin 0 -> 750012 bytes .../loc_11_equipment_storage_sparks.jpg | Bin 0 -> 690465 bytes .../locations/loc_14_core_access_active.jpg | Bin 0 -> 685287 bytes src/NullDepth/wwwroot/css/app.css | 36 +++- src/NullDepth/wwwroot/data/game.json | 15 +- src/NullDepth/wwwroot/localization/en.json | 35 +++- src/NullDepth/wwwroot/localization/sk.json | 35 +++- 19 files changed, 355 insertions(+), 12 deletions(-) create mode 100644 src/NullDepth/wwwroot/assets/audio/ai_architect_01.wav create mode 100644 src/NullDepth/wwwroot/assets/audio/ai_architect_02.wav create mode 100644 src/NullDepth/wwwroot/assets/audio/ai_architect_03.wav create mode 100644 src/NullDepth/wwwroot/assets/audio/ai_architect_04.wav create mode 100644 src/NullDepth/wwwroot/assets/audio/sfx_puzzle_success.wav create mode 100644 src/NullDepth/wwwroot/assets/images/endings/ending_containment.jpg create mode 100644 src/NullDepth/wwwroot/assets/images/endings/ending_release.jpg create mode 100644 src/NullDepth/wwwroot/assets/images/endings/ending_shutdown.webp create mode 100644 src/NullDepth/wwwroot/assets/images/locations/loc_01_exterior_access_open.jpg create mode 100644 src/NullDepth/wwwroot/assets/images/locations/loc_09_cooling_junction_cryo.jpg create mode 100644 src/NullDepth/wwwroot/assets/images/locations/loc_11_equipment_storage_sparks.jpg create mode 100644 src/NullDepth/wwwroot/assets/images/locations/loc_14_core_access_active.jpg diff --git a/src/NullDepth/Game/GameService.cs b/src/NullDepth/Game/GameService.cs index bff10dc..35191c5 100644 --- a/src/NullDepth/Game/GameService.cs +++ b/src/NullDepth/Game/GameService.cs @@ -179,6 +179,7 @@ public sealed class GameService(HttpClient http, IJSRuntime js) if (!HasAll(exit.RequiresFlags)) return T(exit.LockedMessageKey ?? "game.exit.locked"); State.CurrentRoomId = exit.Target; State.VisitedRooms.Add(exit.Target); + await CheckAchievementsAsync(); await SaveAsync(); return ""; } @@ -204,9 +205,47 @@ public sealed class GameService(HttpClient http, IJSRuntime js) } } if (interaction.Checkpoint) SetCheckpoint(); + await CheckAchievementsAsync(); await SaveAsync(); } + public static readonly List AchievementsList = + [ + new() { Id = "ach_descent", TitleKey = "ach.descent.title", DescriptionKey = "ach.descent.desc", Icon = "▼" }, + new() { Id = "ach_cold_blood", TitleKey = "ach.coldBlood.title", DescriptionKey = "ach.coldBlood.desc", Icon = "♥" }, + new() { Id = "ach_electrician", TitleKey = "ach.electrician.title", DescriptionKey = "ach.electrician.desc", Icon = "⚡" }, + new() { Id = "ach_waveform", TitleKey = "ach.waveform.title", DescriptionKey = "ach.waveform.desc", Icon = "〰" }, + new() { Id = "ach_dialogue", TitleKey = "ach.dialogue.title", DescriptionKey = "ach.dialogue.desc", Icon = "◆" }, + new() { Id = "ach_archivist", TitleKey = "ach.archivist.title", DescriptionKey = "ach.archivist.desc", Icon = "📖" }, + new() { Id = "ach_ending_a", TitleKey = "ach.endingA.title", DescriptionKey = "ach.endingA.desc", Icon = "📡" }, + new() { Id = "ach_ending_b", TitleKey = "ach.endingB.title", DescriptionKey = "ach.endingB.desc", Icon = "⏻" }, + new() { Id = "ach_ending_c", TitleKey = "ach.endingC.title", DescriptionKey = "ach.endingC.desc", Icon = "🔒" }, + new() { Id = "ach_survivor", TitleKey = "ach.survivor.title", DescriptionKey = "ach.survivor.desc", Icon = "★" } + ]; + + public async Task UnlockAchievementAsync(string id) + { + if (Profile.UnlockedAchievements.Add(id)) + { + await SaveProfileAsync(); + return true; + } + return false; + } + + public async Task CheckAchievementsAsync() + { + if (State.CurrentRoomId == "loc_08_lower_landing") await UnlockAchievementAsync("ach_descent"); + if (State.Flags.Contains("manual_override_found")) await UnlockAchievementAsync("ach_electrician"); + if (State.Flags.Contains("asked_why") && State.Flags.Contains("asked_staff") && State.Flags.Contains("asked_reconnect")) + await UnlockAchievementAsync("ach_dialogue"); + if (Profile.DiscoveredArchives.Count >= 6) await UnlockAchievementAsync("ach_archivist"); + if (State.CompletedEnding == "release") await UnlockAchievementAsync("ach_ending_a"); + if (State.CompletedEnding == "shutdown") await UnlockAchievementAsync("ach_ending_b"); + if (State.CompletedEnding == "containment") await UnlockAchievementAsync("ach_ending_c"); + if (State.CompletedEnding is not null && CurrentBpm < 100) await UnlockAchievementAsync("ach_cold_blood"); + } + public async Task ApplyChoiceAsync(ChoiceDefinition choice) { Apply(choice.SetFlags, choice.ClearFlags, choice.AddItems); @@ -220,6 +259,7 @@ public sealed class GameService(HttpClient http, IJSRuntime js) } } if (choice.Checkpoint) SetCheckpoint(); + await CheckAchievementsAsync(); await SaveAsync(); } diff --git a/src/NullDepth/Models/GameModels.cs b/src/NullDepth/Models/GameModels.cs index 5d6adfc..a9d8a80 100644 --- a/src/NullDepth/Models/GameModels.cs +++ b/src/NullDepth/Models/GameModels.cs @@ -131,6 +131,15 @@ public sealed class PlayerProfile { public HashSet UnlockedEndings { get; set; } = []; public HashSet DiscoveredArchives { get; set; } = []; + public HashSet UnlockedAchievements { get; set; } = []; +} + +public sealed class AchievementDefinition +{ + public string Id { get; set; } = ""; + public string TitleKey { get; set; } = ""; + public string DescriptionKey { get; set; } = ""; + public string Icon { get; set; } = "★"; } public enum SaveLoadResult diff --git a/src/NullDepth/Pages/Home.razor b/src/NullDepth/Pages/Home.razor index cef0cc3..285f9bb 100644 --- a/src/NullDepth/Pages/Home.razor +++ b/src/NullDepth/Pages/Home.razor @@ -42,7 +42,8 @@ @@ -76,6 +77,23 @@
+ @if (room.Id == "loc_01_exterior_access") + { +
+ } + @if (room.Id is "loc_09_cooling_junction" or "loc_10_cooling_control") + { +
+ } + @if (room.Id == "loc_11_equipment_storage" && !Game.State.Flags.Contains("manual_override_found")) + { +
+ } + @if (room.Id is "loc_14_core_access" or "loc_15_server_core") + { +
+ } + @if (_scanMode) {
@@ -311,8 +329,9 @@

@(selectedItem.DetailedKey is not null ? Game.T(selectedItem.DetailedKey) : Game.T(selectedItem.DescriptionKey))

@if (selectedItem.Id == "diagnostic_scanner") { -
+
+
}
@@ -372,6 +391,71 @@
} + @if (_showWaveformPuzzle) + { + + } + @if (_showCodex) + { + + } @if (_showPause) {