From f1e6899fc955648dfaa6ade316cbde9bf0ba33b4 Mon Sep 17 00:00:00 2001 From: SELEKCJONER Date: Sun, 17 Nov 2024 20:51:20 +0100 Subject: [PATCH] Experimental archive support Note archive event returns to stage 1 if hard mode is played and left unfinished quick battle doesnt work as well Prolouge always plays --- EpinelPS/Database/JsonDb.cs | 2 + EpinelPS/GameData/GameData.cs | 30 ++++++++ EpinelPS/GameData/JsonStaticData.cs | 72 +++++++++++++++++++ .../Msgs/Archive/ArchiveClearStage.cs | 43 +++++++++++ .../Msgs/Archive/ArchiveEnterStage.cs | 18 +++++ .../Msgs/Archive/CompleteArchiveScenario.cs | 38 ++++++++++ .../Msgs/Archive/FastClearArchiveStage.cs | 38 ++++++++++ .../Msgs/Archive/GetArchiveStoryDungeon.cs | 50 +++++++++++++ .../LobbyServer/Msgs/Archive/GetArchives.cs | 17 ++++- .../Msgs/Archive/GetNonResettable.cs | 53 ++++++++++++++ .../LobbyServer/Msgs/Archive/GetResettable.cs | 22 ++++++ 11 files changed, 381 insertions(+), 2 deletions(-) create mode 100644 EpinelPS/LobbyServer/Msgs/Archive/ArchiveClearStage.cs create mode 100644 EpinelPS/LobbyServer/Msgs/Archive/ArchiveEnterStage.cs create mode 100644 EpinelPS/LobbyServer/Msgs/Archive/CompleteArchiveScenario.cs create mode 100644 EpinelPS/LobbyServer/Msgs/Archive/FastClearArchiveStage.cs create mode 100644 EpinelPS/LobbyServer/Msgs/Archive/GetArchiveStoryDungeon.cs create mode 100644 EpinelPS/LobbyServer/Msgs/Archive/GetNonResettable.cs create mode 100644 EpinelPS/LobbyServer/Msgs/Archive/GetResettable.cs diff --git a/EpinelPS/Database/JsonDb.cs b/EpinelPS/Database/JsonDb.cs index 954ebed..b2d9554 100644 --- a/EpinelPS/Database/JsonDb.cs +++ b/EpinelPS/Database/JsonDb.cs @@ -62,6 +62,8 @@ namespace EpinelPS.Database public class EventData { public List CompletedScenarios = new(); + public int Diff = 0; // Default value for Diff + public int LastStage = 0; // Default value for LastStage } public class SynchroSlot diff --git a/EpinelPS/GameData/GameData.cs b/EpinelPS/GameData/GameData.cs index 1522bff..bf32f27 100644 --- a/EpinelPS/GameData/GameData.cs +++ b/EpinelPS/GameData/GameData.cs @@ -48,6 +48,10 @@ namespace EpinelPS.StaticInfo public Dictionary lwptablemgrs = new Dictionary(); // Fixed initialization private Dictionary albumResourceRecords = new Dictionary(); public Dictionary userFrameTable = new Dictionary(); + public Dictionary archiveRecordManagerTable = new Dictionary(); + public Dictionary archiveEventStoryRecords = new Dictionary(); + public Dictionary archiveEventQuestRecords = new Dictionary(); + public Dictionary archiveEventDungeonStageRecords = new Dictionary(); @@ -377,6 +381,12 @@ namespace EpinelPS.StaticInfo { OutpostBattle.Add(obj.id, obj); } + + var archiveRecordManagerTableData = await LoadZip("ArchiveRecordManagerTable.json", progress); + foreach (var obj in archiveRecordManagerTableData.records) + { + archiveRecordManagerTable.Add(obj.id, obj); + } var gachaTypeTable = await LoadZip("GachaTypeTable.json", progress); @@ -407,6 +417,26 @@ namespace EpinelPS.StaticInfo { userFrameTable[record.id] = record; } + // Load and parse ArchiveEventDungeonStageTable.json + var archiveEventDungeonStageData = await LoadZip("ArchiveEventDungeonStageTable.json", progress); + foreach (var obj in archiveEventDungeonStageData.records) + { + archiveEventDungeonStageRecords.Add(obj.id, obj); + } + + // Load and parse ArchiveEventStoryTable.json + var archiveEventStoryTable = await LoadZip("ArchiveEventStoryTable.json", progress); + foreach (var obj in archiveEventStoryTable.records) + { + archiveEventStoryRecords.Add(obj.id, obj); + } + + // Load and parse ArchiveEventQuestTable.json + var archiveEventQuestTable = await LoadZip("ArchiveEventQuestTable.json", progress); + foreach (var obj in archiveEventQuestTable.records) + { + archiveEventQuestRecords.Add(obj.id, obj); + } var albumResourceTable = await LoadZip("AlbumResourceTable.json", progress); foreach (var obj in albumResourceTable.records) diff --git a/EpinelPS/GameData/JsonStaticData.cs b/EpinelPS/GameData/JsonStaticData.cs index baad90f..95dee85 100644 --- a/EpinelPS/GameData/JsonStaticData.cs +++ b/EpinelPS/GameData/JsonStaticData.cs @@ -317,5 +317,77 @@ { public List records; } + + public class ArchiveRecordManagerRecord + { + public int id; + public string record_type; + public string record_title_locale; + public int record_main_archive_event_id; + public int record_list_order; + public int unlock_ticket_id; + public int unlock_ticket_count; + public int reward_info_list_id; + public int event_quest_clear_reward_id; + public int recommended_story_list_id; + public int included_contents_group_id; + public string record_slot_bg_addressable; + public string record_unlock_bg_addressable; + } + + public class ArchiveRecordManagerTable + { + public List records; + } + + public class ArchiveEventStoryRecord + { + public int id; + public int event_id; + public string prologue_scenario = ""; + public int dungeon_id; + public int album_category_group; + public string ui_prefab = ""; + public int archive_ticket_item_id; + public int archive_currency_item_id; + } + + public class ArchiveEventStoryTable + { + public List records; + } + + public class ArchiveEventQuestRecord + { + public int id; + public int event_quest_manager_id; + public string condition_type = ""; + public int condition_value; + public string name_localkey = ""; + public string description_localkey = ""; + public int next_quest_id; + public string end_scenario_id = ""; + } + + public class ArchiveEventQuestTable + { + public List records; + } + + public class ArchiveEventDungeonStageRecord + { + public int id; + public int group; + public int step; + public string stage_name = ""; + public string stage_contents_type = ""; + public int stage_id; + public bool is_repeat_clear; + } + + public class ArchiveEventDungeonStageTable + { + public List records; + } } diff --git a/EpinelPS/LobbyServer/Msgs/Archive/ArchiveClearStage.cs b/EpinelPS/LobbyServer/Msgs/Archive/ArchiveClearStage.cs new file mode 100644 index 0000000..1015e16 --- /dev/null +++ b/EpinelPS/LobbyServer/Msgs/Archive/ArchiveClearStage.cs @@ -0,0 +1,43 @@ +using EpinelPS.Utils; +using EpinelPS.Database; +namespace EpinelPS.LobbyServer.Msgs.Archive +{ + [PacketPath("/archive/storydungeon/clearstage")] + public class ClearArchiveStage : LobbyMsgHandler + { + protected override async Task HandleAsync() + { + var req = await ReadData(); // has fields EventId, StageId, BattleResult + var evid = req.EventId; + var stgid = req.StageId; + var result = req.BattleResult; // if 2 add to event info as last stage + var user = GetUser(); + + if (user == null) + { + throw new Exception("User not found."); + } + + // Check if the EventInfo exists for the given EventId + if (!user.EventInfo.ContainsKey(evid)) + { + throw new Exception($"Event with ID {evid} not found."); + } + + // Update the EventData if BattleResult is 2 + if (result == 1) + { + var eventData = user.EventInfo[evid]; + + // Update the LastStage in EventData + eventData.LastStage = stgid; + + } + JsonDb.Save(); + var response = new ResClearArchiveStage(); + + // Send the response back to the client + await WriteDataAsync(response); + } + } +} diff --git a/EpinelPS/LobbyServer/Msgs/Archive/ArchiveEnterStage.cs b/EpinelPS/LobbyServer/Msgs/Archive/ArchiveEnterStage.cs new file mode 100644 index 0000000..e050bcd --- /dev/null +++ b/EpinelPS/LobbyServer/Msgs/Archive/ArchiveEnterStage.cs @@ -0,0 +1,18 @@ +using EpinelPS.Utils; + +namespace EpinelPS.LobbyServer.Msgs.Archive +{ + [PacketPath("/archive/storydungeon/enterstage")] + public class EnterArchiveStage : LobbyMsgHandler + { + protected override async Task HandleAsync() + { + var req = await ReadData();// has fields EventId StageId TeamNumber + var evid = req.EventId; + + var response = new ResEnterArchiveStage(); + + await WriteDataAsync(response); + } + } +} diff --git a/EpinelPS/LobbyServer/Msgs/Archive/CompleteArchiveScenario.cs b/EpinelPS/LobbyServer/Msgs/Archive/CompleteArchiveScenario.cs new file mode 100644 index 0000000..c0d3243 --- /dev/null +++ b/EpinelPS/LobbyServer/Msgs/Archive/CompleteArchiveScenario.cs @@ -0,0 +1,38 @@ +using EpinelPS.Utils; +using EpinelPS.Database; +using EpinelPS.StaticInfo; // Ensure this namespace is included + +namespace EpinelPS.LobbyServer.Msgs.Archive +{ + [PacketPath("/archive/scenario/complete")] + public class CompleteScenario : LobbyMsgHandler + { + protected override async Task HandleAsync() + { + var req = await ReadData(); // req has EventId, ScenarioId, DialogType fields + var evid = req.EventId; + var scenid = req.ScenarioId; + var dialtyp = req.DialogType; + + var user = GetUser(); + + // Ensure we are working with the user's EventInfo and not CompletedScenarios + if (!user.EventInfo.TryGetValue(evid, out var evt)) + { + // Create a new EventData if the event doesn't exist + evt = new EventData(); + user.EventInfo[evid] = evt; + } + + // Ensure the CompletedScenarios list is initialized and add the ScenarioId + if (!evt.CompletedScenarios.Contains(scenid)) + { + evt.CompletedScenarios.Add(scenid); + } + JsonDb.Save(); + // Prepare and send the response + var response = new ResCompleteArchiveScenario(); + await WriteDataAsync(response); + } + } +} diff --git a/EpinelPS/LobbyServer/Msgs/Archive/FastClearArchiveStage.cs b/EpinelPS/LobbyServer/Msgs/Archive/FastClearArchiveStage.cs new file mode 100644 index 0000000..750776b --- /dev/null +++ b/EpinelPS/LobbyServer/Msgs/Archive/FastClearArchiveStage.cs @@ -0,0 +1,38 @@ +using EpinelPS.Utils; +using EpinelPS.Database; +namespace EpinelPS.LobbyServer.Msgs.Archive +{ + [PacketPath("/archive/storydungeon/fastclearstage")] + public class FastClearArchiveStage : LobbyMsgHandler + { + protected override async Task HandleAsync() + { + var req = await ReadData(); + var evid = req.EventId; + var stgid = req.StageId; + + var user = GetUser(); + + if (user == null) + { + throw new Exception("User not found."); + } + + // Check if the EventInfo exists for the given EventId + if (!user.EventInfo.ContainsKey(evid)) + { + throw new Exception($"Event with ID {evid} not found."); + } + + var eventData = user.EventInfo[evid]; + // Update the LastStage in EventData + eventData.LastStage = stgid; + + JsonDb.Save(); + var response = new ResFastClearArchiveStage(); + + // Send the response back to the client + await WriteDataAsync(response); + } + } +} diff --git a/EpinelPS/LobbyServer/Msgs/Archive/GetArchiveStoryDungeon.cs b/EpinelPS/LobbyServer/Msgs/Archive/GetArchiveStoryDungeon.cs new file mode 100644 index 0000000..d2c6b24 --- /dev/null +++ b/EpinelPS/LobbyServer/Msgs/Archive/GetArchiveStoryDungeon.cs @@ -0,0 +1,50 @@ +using EpinelPS.Utils; +using EpinelPS.Database; + +namespace EpinelPS.LobbyServer.Msgs.Archive +{ + [PacketPath("/archive/storydungeon/get")] + public class GetArchiveStoryDungeon : LobbyMsgHandler + { + protected override async Task HandleAsync() + { + var req = await ReadData(); // has EventId field + var evid = req.EventId; + + // Retrieve the user based on session (assuming GetCurrentUser is defined elsewhere) + var user = GetUser(); + if (user == null) + { + throw new Exception("User not found."); + } + + // Access EventData directly + if (!user.EventInfo.ContainsKey(evid)) + { + throw new Exception($"Event with ID {evid} not found."); + } + + var eventData = user.EventInfo[evid]; + + // Prepare the response + var response = new ResGetArchiveStoryDungeon(); + + // Populate team data + response.TeamData = new NetUserTeamData() + { + LastContentsTeamNumber = 1, + Type = 1 + }; + + // Populate the last cleared stage + response.LastClearedArchiveStageList.Add(new NetLastClearedArchiveStage() + { + DifficultyId = eventData.Diff, + StageId = eventData.LastStage + }); + + // Send the response + await WriteDataAsync(response); + } + } +} diff --git a/EpinelPS/LobbyServer/Msgs/Archive/GetArchives.cs b/EpinelPS/LobbyServer/Msgs/Archive/GetArchives.cs index 31b4391..44a7024 100644 --- a/EpinelPS/LobbyServer/Msgs/Archive/GetArchives.cs +++ b/EpinelPS/LobbyServer/Msgs/Archive/GetArchives.cs @@ -1,4 +1,5 @@ using EpinelPS.Utils; +using EpinelPS.StaticInfo; namespace EpinelPS.LobbyServer.Msgs.Archive { @@ -10,8 +11,20 @@ namespace EpinelPS.LobbyServer.Msgs.Archive var req = await ReadData(); var response = new ResGetArchiveRecord(); - response.ArchiveRecordManagerList.AddRange([100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, 1800]); - response.UnlockedArchiveRecordList.AddRange([100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, 1800]); + + // Explicitly select IDs from the records + var allIds = GameData.Instance.archiveRecordManagerTable.Values.Select(record => record.id).ToList(); + + // Add the IDs to the response lists + response.ArchiveRecordManagerList.AddRange(allIds); + response.UnlockedArchiveRecordList.AddRange(allIds); + + // Get entries with record_type "EventQuest" + var eventQuestRecords = GameData.Instance.archiveRecordManagerTable.Values.Where(record => record.record_type == "EventQuest").ToList(); + + response.UnlockedArchiveEventQuestList.AddRange(eventQuestRecords.Select(record => record.id)); + + // TODO: allow unlocking await WriteDataAsync(response); } diff --git a/EpinelPS/LobbyServer/Msgs/Archive/GetNonResettable.cs b/EpinelPS/LobbyServer/Msgs/Archive/GetNonResettable.cs new file mode 100644 index 0000000..d1be12a --- /dev/null +++ b/EpinelPS/LobbyServer/Msgs/Archive/GetNonResettable.cs @@ -0,0 +1,53 @@ +using EpinelPS.Utils; +using EpinelPS.StaticInfo; // Ensure this namespace is included + + +namespace EpinelPS.LobbyServer.Msgs.Archive +{ + [PacketPath("/archive/scenario/getnonresettable")] + public class GetNonResettable : LobbyMsgHandler + { + protected override async Task HandleAsync() + { + var req = await ReadData(); // req has EventId field + var evid = req.EventId; + var response = new ResGetNonResettableArchiveScenario(); + + // Access the GameData instance + var gameData = GameData.Instance; + + if (evid == 130002) + { + // Directly use the archiveEventQuestRecords dictionary + foreach (var record in gameData.archiveEventQuestRecords.Values) + { + if (record.event_quest_manager_id == evid) + { + // Add the end_scenario_id to the ScenarioIdList + if (!string.IsNullOrEmpty(record.end_scenario_id)) + { + response.ScenarioIdList.Add(record.end_scenario_id); + } + } + } + } + else + { + // Directly use the archiveEventStoryRecords dictionary + foreach (var record in gameData.archiveEventStoryRecords.Values) + { + if (record.event_id == evid) + { + // Add the prologue_scenario to the ScenarioIdList + if (!string.IsNullOrEmpty(record.prologue_scenario)) + { + response.ScenarioIdList.Add(record.prologue_scenario); + } + } + } + } + + await WriteDataAsync(response); + } + } +} diff --git a/EpinelPS/LobbyServer/Msgs/Archive/GetResettable.cs b/EpinelPS/LobbyServer/Msgs/Archive/GetResettable.cs new file mode 100644 index 0000000..8999350 --- /dev/null +++ b/EpinelPS/LobbyServer/Msgs/Archive/GetResettable.cs @@ -0,0 +1,22 @@ +using EpinelPS.Utils; +using EpinelPS.StaticInfo; +namespace EpinelPS.LobbyServer.Msgs.Archive +{ + [PacketPath("/archive/scenario/getresettable")] + public class GetResettable : LobbyMsgHandler + { + protected override async Task HandleAsync() + { + var req = await ReadData(); + var response = new ResGetResettableArchiveScenario(); // has ScenarioIdList field that takes in strings + + // Retrieve stage IDs from GameData + var stageIds = GameData.Instance.archiveEventDungeonStageRecords.Values.Select(record => record.stage_id.ToString()).ToList(); + + // Add them to the response + response.ScenarioIdList.Add(stageIds); + + await WriteDataAsync(response); + } + } +}