From 5665e3306c8ea549ba4e27fa42b1cb1eb365ba7d Mon Sep 17 00:00:00 2001 From: Zenny Date: Mon, 8 Jun 2026 16:00:27 -0700 Subject: [PATCH] Add unlock conditions for gallery --- .../Entity/IEnemy.cs | 2 - Zennysoft.Game.Ma/src/app/App.cs | 138 +++++++++++++++++- Zennysoft.Game.Ma/src/enemy/Enemy.cs | 11 +- Zennysoft.Game.Ma/src/game/Game.cs | 49 ++++++- Zennysoft.Game.Ma/src/game/IGame.cs | 12 ++ .../src/map/dungeon/code/FinalFloor.cs | 12 +- .../dungeon/floors/Special Floors/BadEnd.cs | 3 +- Zennysoft.Game.Ma/src/npc/Stele.cs | 12 +- Zennysoft.Game.Ma/src/options/GalleryData.cs | 6 +- .../src/save/MaSaveFileManager.cs | 11 +- .../src/ui/gallery/GalleryMenu.cs | 82 +++++++---- 11 files changed, 268 insertions(+), 70 deletions(-) diff --git a/Zennysoft.Game.Ma.Implementation/Entity/IEnemy.cs b/Zennysoft.Game.Ma.Implementation/Entity/IEnemy.cs index 983df546..88d3a751 100644 --- a/Zennysoft.Game.Ma.Implementation/Entity/IEnemy.cs +++ b/Zennysoft.Game.Ma.Implementation/Entity/IEnemy.cs @@ -27,8 +27,6 @@ namespace Zennysoft.Ma.Adapter.Entity public void OnHealed(); - public void IncrementDefeatCount(); - public int GetDefeatCount(IEnemy enemyType); public IDungeonRoom GetCurrentRoom(ImmutableList dungeonRooms); diff --git a/Zennysoft.Game.Ma/src/app/App.cs b/Zennysoft.Game.Ma/src/app/App.cs index f5418b98..1d32de61 100644 --- a/Zennysoft.Game.Ma/src/app/App.cs +++ b/Zennysoft.Game.Ma/src/app/App.cs @@ -1,9 +1,12 @@ using Chickensoft.AutoInject; using Chickensoft.GodotNodeInterfaces; using Chickensoft.Introspection; +using Chickensoft.SaveFileBuilder; +using Chickensoft.Serialization; using Godot; using NathanHoad; using SimpleInjector.Lifestyles; +using System.IO; using System.IO.Abstractions; using System.Threading.Tasks; using Zennysoft.Game.Abstractions; @@ -12,10 +15,13 @@ using Zennysoft.Ma.Adapter; namespace Zennysoft.Game.Ma; -public interface IApp : INode, IProvide; +public interface IApp : INode, IProvide, IProvide +{ + public GalleryData GalleryData { get; } +} [Meta(typeof(IAutoNode))] -public partial class App : Node, IApp +public partial class App : Node, IApp, IProvide> { public override void _Notification(int what) => this.Notify(what); @@ -33,22 +39,31 @@ public partial class App : Node, IApp IAppRepo IProvide.Value() => AppRepo; + IApp IProvide.Value() => this; + public IAppRepo AppRepo { get; set; } = default!; public IAppLogic AppLogic { get; set; } = default!; public AppLogic.IBinding AppBinding { get; set; } = default!; private Godot.Collections.Array _progress; private SimpleInjector.Container _container; - + private IMaSaveFileManager _gallerySaveDataManager; private EnemyViewer _dataViewer; private bool _loadingGame = false; private bool _loadingEnemyViewer = false; private string _optionsSavePath = string.Empty; private string _controllerSavePath = string.Empty; + private string _gallerySavePath = string.Empty; private ISaveFileManager _saveFileManager; private IGame _game; private IEnemyViewer _enemyViewer; + public ISaveChunk GameChunk { get; set; } = default!; + ISaveChunk IProvide>.Value() => GameChunk; + public ISaveFile GallerySaveFile { get; set; } = default!; + + public GalleryData GalleryData { get; private set; } + private double _reportedProgress = 0; public void Initialize() @@ -63,6 +78,64 @@ public partial class App : Node, IApp _saveFileManager = _container.GetInstance(); _optionsSavePath = $"{OS.GetUserDataDir()}/options.json"; _controllerSavePath = $"{OS.GetUserDataDir()}/controls.json"; + _gallerySavePath = $"{OS.GetUserDataDir()}/gallery.json"; + + GalleryData = new GalleryData(); + + GameChunk = new SaveChunk( + (chunk) => + { + var galleryData = new GalleryData() + { + SproingyFifty = GalleryData.SproingyFifty, + MichaelFifty = GalleryData.MichaelFifty, + FilthEaterFifty = GalleryData.FilthEaterFifty, + SaraFifty = GalleryData.SaraFifty, + BallosFifty = GalleryData.BallosFifty, + PlanterFifty = GalleryData.PlanterFifty, + ChintheFifty = GalleryData.ChintheFifty, + AmbassadorGreenFifty = GalleryData.AmbassadorGreenFifty, + AmbassadorRedFifty = GalleryData.AmbassadorRedFifty, + AmbassadorSteelFifty = GalleryData.AmbassadorSteelFifty, + AgniDemonFifty = GalleryData.AgniDemonFifty, + AqueousDemonFifty = GalleryData.AqueousDemonFifty, + EdenPillarFifty = GalleryData.EdenPillarFifty, + PalanFifty = GalleryData.PalanFifty, + ShieldOfHeavenFifty = GalleryData.ShieldOfHeavenFifty, + GoldSproingFifty = GalleryData.GoldSproingFifty, + TotalEnemiesFifty = GalleryData.TotalEnemiesFifty, + TotalEnemiesHundred = GalleryData.TotalEnemiesHundred, + NormalEnd = GalleryData.NormalEnd, + TrueEnd = GalleryData.TrueEnd, + BadEnd = GalleryData.BadEnd, + StelesTen = GalleryData.StelesTen + }; + return galleryData; + }, + onLoad: + (chunk, data) => + { + GalleryData = data ?? new GalleryData(); + }); + + _gallerySaveDataManager = new MaSaveFileManager(_gallerySavePath); + GallerySaveFile = new SaveFile( + root: GameChunk, + onSave: _gallerySaveDataManager.Save, + onLoad: async () => + { + try + { + var galleryData = await _gallerySaveDataManager.Load(); + return galleryData; + } + catch (FileNotFoundException) + { + GD.Print("No save file found."); + } + return null; + } + ); MainMenu.StartGame += OnStartGame; MainMenu.EnemyViewer += OnEnemyViewer; @@ -119,7 +192,7 @@ public partial class App : Node, IApp MainMenu.OptionsButton.GrabFocus(); } - private void GalleryExited() + private async void GalleryExited() { GalleryMenu.Hide(); MainMenu.GalleryButton.GrabFocus(); @@ -204,6 +277,7 @@ public partial class App : Node, IApp _game.GameLoaded += OnGameLoaded; _game.GameExitRequested += GameExitRequested; CallDeferred(MethodName.AddChild, scene); + _game.UnlockGalleryItem += UnlockGalleryItem; } private void OnGameLoaded() => LoadingScreen.HideLoadingScreen(); @@ -216,6 +290,56 @@ public partial class App : Node, IApp LoadingScreen.HideLoadingScreen(); } + private void UnlockGalleryItem(string obj) + { + if (obj == nameof(GalleryData.SproingyFifty)) + GalleryData.SproingyFifty = true; + if (obj == nameof(GalleryData.MichaelFifty)) + GalleryData.MichaelFifty = true; + if (obj == nameof(GalleryData.FilthEaterFifty)) + GalleryData.FilthEaterFifty = true; + if (obj == nameof(GalleryData.SaraFifty)) + GalleryData.SaraFifty = true; + if (obj == nameof(GalleryData.BallosFifty)) + GalleryData.BallosFifty = true; + if (obj == nameof(GalleryData.PlanterFifty)) + GalleryData.PlanterFifty = true; + if (obj == nameof(GalleryData.ChintheFifty)) + GalleryData.ChintheFifty = true; + if (obj == nameof(GalleryData.AmbassadorGreenFifty)) + GalleryData.AmbassadorGreenFifty = true; + if (obj == nameof(GalleryData.AmbassadorRedFifty)) + GalleryData.AmbassadorRedFifty = true; + if (obj == nameof(GalleryData.AmbassadorSteelFifty)) + GalleryData.AmbassadorSteelFifty = true; + if (obj == nameof(GalleryData.AgniDemonFifty)) + GalleryData.AgniDemonFifty = true; + if (obj == nameof(GalleryData.AqueousDemonFifty)) + GalleryData.AqueousDemonFifty = true; + if (obj == nameof(GalleryData.EdenPillarFifty)) + GalleryData.EdenPillarFifty = true; + if (obj == nameof(GalleryData.PalanFifty)) + GalleryData.PalanFifty = true; + if (obj == nameof(GalleryData.ShieldOfHeavenFifty)) + GalleryData.ShieldOfHeavenFifty = true; + if (obj == nameof(GalleryData.GoldSproingFifty)) + GalleryData.GoldSproingFifty = true; + if (obj == nameof(GalleryData.TotalEnemiesFifty)) + GalleryData.TotalEnemiesFifty = true; + if (obj == nameof(GalleryData.TotalEnemiesHundred)) + GalleryData.TotalEnemiesHundred = true; + if (obj == nameof(GalleryData.NormalEnd)) + GalleryData.NormalEnd = true; + if (obj == nameof(GalleryData.TrueEnd)) + GalleryData.TrueEnd = true; + if (obj == nameof(GalleryData.BadEnd)) + GalleryData.BadEnd = true; + if (obj == nameof(GalleryData.StelesTen)) + GalleryData.StelesTen = true; + + GallerySaveFile.Save(); + } + private async Task LoadSceneInternal(string sceneName) { LoadingScreen.ShowLoadingScreen(); @@ -239,7 +363,11 @@ public partial class App : Node, IApp OptionsMenu.GameTab.GrabFocus(); } - private async void OnGallery() => GalleryMenu.Show(); + private async void OnGallery() + { + GalleryData = await _gallerySaveDataManager.Load(); + GalleryMenu.Show(); + } public void OnQuit() => AppLogic.Input(new AppLogic.Input.QuitGame()); diff --git a/Zennysoft.Game.Ma/src/enemy/Enemy.cs b/Zennysoft.Game.Ma/src/enemy/Enemy.cs index 1351d893..f0606aeb 100644 --- a/Zennysoft.Game.Ma/src/enemy/Enemy.cs +++ b/Zennysoft.Game.Ma/src/enemy/Enemy.cs @@ -190,8 +190,7 @@ public abstract partial class Enemy : CharacterBody3D, IEnemy, IProvide InventoryEventNotification; + + public event Action UnlockGalleryItem; + public Game() { _container = new SimpleInjector.Container(); @@ -105,6 +108,7 @@ public partial class Game : Node3D, IGame RescuedItems = new RescuedItemDatabase(20); SarcoData = new SarcoData(); NpcData = new NpcData() { SteleDiscovered = [] }; + StatData = new StatData(); ItemDatabase = ItemDatabase.Instance; GameChunk = new SaveChunk( @@ -150,7 +154,7 @@ public partial class Game : Node3D, IGame } ); - var saveFileManager = new MaSaveFileManager(); + var saveFileManager = new MaSaveFileManager($"{OS.GetUserDataDir()}/game.json"); SaveFile = new SaveFile( root: GameChunk, onSave: saveFileManager.Save, @@ -956,6 +960,49 @@ public partial class Game : Node3D, IGame public void NotifyInventory(string message) => InventoryEventNotification?.Invoke(message); + public async Task IncrementEnemyDefeatedCount(string enemyName) + { + if (StatData.EnemiesDefeated.ContainsKey(enemyName)) + StatData.EnemiesDefeated[enemyName]++; + else + StatData.EnemiesDefeated.TryAdd(enemyName, 1); + + if (StatData.EnemiesDefeated[enemyName] == 50) + UnlockGalleryItem.Invoke(enemyName + "Fifty"); + if (StatData.EnemiesDefeated[enemyName] == 100) + UnlockGalleryItem.Invoke(enemyName + "Hundred"); + + await Save(); + } + + public async Task IncrementSteleCount(int steleId) + { + if (!NpcData.SteleDiscovered.Contains(steleId)) + { + NpcData.SteleDiscovered.Add(steleId); + + if (NpcData.SteleDiscovered.Count == 10) + UnlockGalleryItem.Invoke("StelesTen"); + + await Save(); + } + } + + public async Task OnReachNormalEnd() + { + UnlockGalleryItem.Invoke("NormalEnd"); + } + + public async Task OnReachBadEnd() + { + UnlockGalleryItem.Invoke("BadEnd"); + } + + public async Task OnReachTrueEnd() + { + UnlockGalleryItem.Invoke("TrueEnd"); + } + private void OnQuit() => GameExitRequested?.Invoke(); private void GameOverMenuAppeared() => _map.ClearFloor(); diff --git a/Zennysoft.Game.Ma/src/game/IGame.cs b/Zennysoft.Game.Ma/src/game/IGame.cs index dfa365cd..15fc77b3 100644 --- a/Zennysoft.Game.Ma/src/game/IGame.cs +++ b/Zennysoft.Game.Ma/src/game/IGame.cs @@ -42,6 +42,16 @@ public interface IGame : IProvide, IProvide, IProvide public void NotifyInventory(string message); + public Task IncrementEnemyDefeatedCount(string enemyName); + + public Task IncrementSteleCount(int steleId); + + public Task OnReachNormalEnd(); + + public Task OnReachBadEnd(); + + public Task OnReachTrueEnd(); + public ItemRescueMenu ItemRescueMenu { get; } public QuestData QuestData { get; } @@ -57,4 +67,6 @@ public interface IGame : IProvide, IProvide, IProvide public event Action GameLoaded; public event Action InventoryEventNotification; + + public event Action UnlockGalleryItem; } diff --git a/Zennysoft.Game.Ma/src/map/dungeon/code/FinalFloor.cs b/Zennysoft.Game.Ma/src/map/dungeon/code/FinalFloor.cs index 08d1c056..ad22e0fb 100644 --- a/Zennysoft.Game.Ma/src/map/dungeon/code/FinalFloor.cs +++ b/Zennysoft.Game.Ma/src/map/dungeon/code/FinalFloor.cs @@ -3,7 +3,6 @@ using Chickensoft.Introspection; using Godot; using System.Linq; using Zennysoft.Ma.Adapter; -using static System.Net.Mime.MediaTypeNames; namespace Zennysoft.Game.Ma; @@ -13,21 +12,24 @@ public partial class FinalFloor : SpecialFloor public override void _Notification(int what) => this.Notify(what); [Dependency] protected IPlayer _player => this.DependOn(() => GetParent().GetChildren().OfType().Single()); + [Dependency] protected IGame _game => this.DependOn(); [Node] public Area3D Exit { get; set; } public void OnReady() { - Exit.AreaEntered += Exit_AreaEntered; + Exit.AreaEntered += Exit_AreaEntered; } - private void Exit_AreaEntered(Area3D area) + private async void Exit_AreaEntered(Area3D area) { - _player.Die(); + await _game.OnReachNormalEnd(); + // Start Ending Cutscene + _player.Die(); } public void OnExitTree() { - Exit.AreaEntered -= Exit_AreaEntered; + Exit.AreaEntered -= Exit_AreaEntered; } } diff --git a/Zennysoft.Game.Ma/src/map/dungeon/floors/Special Floors/BadEnd.cs b/Zennysoft.Game.Ma/src/map/dungeon/floors/Special Floors/BadEnd.cs index 5b90f2bc..15a0b4e0 100644 --- a/Zennysoft.Game.Ma/src/map/dungeon/floors/Special Floors/BadEnd.cs +++ b/Zennysoft.Game.Ma/src/map/dungeon/floors/Special Floors/BadEnd.cs @@ -28,11 +28,12 @@ public partial class BadEnd : SpecialFloor tweener.TweenMethod(Callable.From((float x) => SetShakeValue(x)), ShakeAmount, 1f, 700f).SetDelay(30); } - public void StartEndGameCutscene() + public async void StartEndGameCutscene() { _player.Deactivate(); _player.ShowCamera(false); Camera3D.Current = true; + await _game.OnReachBadEnd(); } public void EndGame() diff --git a/Zennysoft.Game.Ma/src/npc/Stele.cs b/Zennysoft.Game.Ma/src/npc/Stele.cs index 5620057a..26f4d2c8 100644 --- a/Zennysoft.Game.Ma/src/npc/Stele.cs +++ b/Zennysoft.Game.Ma/src/npc/Stele.cs @@ -10,9 +10,9 @@ namespace Zennysoft.Game.Ma; public partial class Stele : Node3D { public override void _Notification(int what) => this.Notify(what); - + [Dependency] public IGame _game => this.DependOn(); - + [Node] public AnimationPlayer AnimationPlayer { get; set; } [Node] public Area3D DialogueZone { get; set; } = default!; @@ -77,12 +77,8 @@ public partial class Stele : Node3D public async Task EndDialogue() { - if (!_game.NpcData.SteleDiscovered.Contains(ID)) - { - _game.NpcData.SteleDiscovered.Add(ID); - await _game.Save(); - AnimationPlayer.Play("fade_in"); - } + await _game.IncrementSteleCount(ID); + AnimationPlayer.Play("fade_in"); } public override int GetHashCode() => GetPath().GetHashCode(); diff --git a/Zennysoft.Game.Ma/src/options/GalleryData.cs b/Zennysoft.Game.Ma/src/options/GalleryData.cs index 9e0978d5..cf76a2e1 100644 --- a/Zennysoft.Game.Ma/src/options/GalleryData.cs +++ b/Zennysoft.Game.Ma/src/options/GalleryData.cs @@ -12,7 +12,7 @@ public partial class GalleryData : Node [Save("MichaelFifty")] public bool MichaelFifty { get; set; } = false; [Save("FilthEaterFifty")] - public bool FilthEaterFifty { get; set; } = true; + public bool FilthEaterFifty { get; set; } = false; [Save("SaraFifty")] public bool SaraFifty { get; set; } = false; [Save("BallosFifty")] @@ -20,7 +20,7 @@ public partial class GalleryData : Node [Save("PlanterFifty")] public bool PlanterFifty { get; set; } = false; [Save("ChintheFifty")] - public bool ChintheFifty { get; set; } = true; + public bool ChintheFifty { get; set; } = false; [Save("AmbassadorGreenFifty")] public bool AmbassadorGreenFifty { get; set; } = false; [Save("AmbassadorRedFifty")] @@ -40,7 +40,7 @@ public partial class GalleryData : Node [Save("GoldSproingyFifty")] public bool GoldSproingFifty { get; set; } = false; [Save("TotalEnemiesFifty")] - public bool TotalEnemiesFifty { get; set; } = true; + public bool TotalEnemiesFifty { get; set; } = false; [Save("TotalEnemiesHundred")] public bool TotalEnemiesHundred { get; set; } = false; [Save("NormalEnd")] diff --git a/Zennysoft.Game.Ma/src/save/MaSaveFileManager.cs b/Zennysoft.Game.Ma/src/save/MaSaveFileManager.cs index 57ebea6a..6763096a 100644 --- a/Zennysoft.Game.Ma/src/save/MaSaveFileManager.cs +++ b/Zennysoft.Game.Ma/src/save/MaSaveFileManager.cs @@ -9,7 +9,7 @@ namespace Zennysoft.Ma.Adapter; public interface IMaSaveFileManager { - Task Save(T gameData); + Task Save(T data); Task Load(); } @@ -18,17 +18,20 @@ public sealed class MaSaveFileManager : IMaSaveFileManager { private readonly ISaveFileManager _saveFileManager; private readonly ImmutableList _converters; + private string _filePath = string.Empty; - public MaSaveFileManager() + public MaSaveFileManager(string filePath) { _saveFileManager = new SaveFileManager(new FileSystem()); _converters = [WeaponTagEnumContext.Default, ItemTagEnumContext.Default, ElementTypeEnumContext.Default, AccessoryTagEnumContext.Default, UsableItemTagEnumContext.Default, BoxItemTagEnumContext.Default, JewelTagsEnumContext.Default, BaseInventoryItemContext.Default, RescuedItemDatabaseContext.Default]; + _filePath = filePath; + } public async Task Save(T gameData) { - await _saveFileManager.WriteToFile(gameData, [.. _converters]); + await _saveFileManager.WriteToFile(gameData, _filePath, [.. _converters]); } - public async Task Load() => await _saveFileManager.ReadFromFile([.. _converters]); + public async Task Load() => await _saveFileManager.ReadFromFile(_filePath, [.. _converters]); } diff --git a/Zennysoft.Game.Ma/src/ui/gallery/GalleryMenu.cs b/Zennysoft.Game.Ma/src/ui/gallery/GalleryMenu.cs index 0527ea97..e4c41b3d 100644 --- a/Zennysoft.Game.Ma/src/ui/gallery/GalleryMenu.cs +++ b/Zennysoft.Game.Ma/src/ui/gallery/GalleryMenu.cs @@ -1,13 +1,10 @@ using Chickensoft.AutoInject; using Chickensoft.Introspection; -using Chickensoft.Serialization; using Godot; -using NathanHoad; -using Org.BouncyCastle.Asn1.X509; using System.Collections.Generic; using System.Collections.Immutable; -using System.IO; using System.Linq; +using Zennysoft.Game.Abstractions; using Zennysoft.Game.Ma; [Meta(typeof(IAutoNode))] @@ -40,6 +37,8 @@ public partial class GalleryMenu : Control [Node] public TextureRect ItemThumb10 { get; set; } #endregion + [Dependency] public IApp _app => this.DependOn(); + [Node] public TextureButton PreviousButton { get; set; } [Node] public TextureButton NextButton { get; set; } [Node] public TextureButton BackButton { get; set; } @@ -48,8 +47,6 @@ public partial class GalleryMenu : Control [Node] public Control ThumbnailView { get; set; } [Node] public TextureRect FullSizeImage { get; set; } - public GalleryData GalleryData { get; set; } - [Signal] public delegate void GalleryExitedEventHandler(); private List