From c3b675dc34bbfefe2914fc98a79353cb5771d93e Mon Sep 17 00:00:00 2001 From: Kei-Luna Date: Sun, 24 May 2026 08:14:19 +0900 Subject: [PATCH] VirCapture can Enter --- Common/Data/Excel/VirCaptureSeasonExcel.cs | 18 ++ Common/Data/Excel/VirCaptureTimeExcel.cs | 18 ++ Common/Data/Excel/VirCaptureTrialTimeExcel.cs | 19 ++ Common/Data/GameData.cs | 3 + .../VirCapture/VirCapture_CheckOpenAct.cs | 164 ++++++++++++++++++ 5 files changed, 222 insertions(+) create mode 100644 Common/Data/Excel/VirCaptureSeasonExcel.cs create mode 100644 Common/Data/Excel/VirCaptureTimeExcel.cs create mode 100644 Common/Data/Excel/VirCaptureTrialTimeExcel.cs create mode 100644 GameServer/Server/CallGS/Handlers/VirCapture/VirCapture_CheckOpenAct.cs diff --git a/Common/Data/Excel/VirCaptureSeasonExcel.cs b/Common/Data/Excel/VirCaptureSeasonExcel.cs new file mode 100644 index 0000000..22a3ab0 --- /dev/null +++ b/Common/Data/Excel/VirCaptureSeasonExcel.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace MikuSB.Data.Excel; + +[ResourceEntity("dlc/vircapture/season.json")] +public class VirCaptureSeasonExcel : ExcelResource +{ + [JsonProperty("Id")] public uint Id { get; set; } + [JsonProperty("StartTime")] public string StartTime { get; set; } = ""; + [JsonProperty("EndTime")] public string EndTime { get; set; } = ""; + + public override uint GetId() => Id; + + public override void Loaded() + { + GameData.VirCaptureSeasonData[Id] = this; + } +} diff --git a/Common/Data/Excel/VirCaptureTimeExcel.cs b/Common/Data/Excel/VirCaptureTimeExcel.cs new file mode 100644 index 0000000..2195228 --- /dev/null +++ b/Common/Data/Excel/VirCaptureTimeExcel.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace MikuSB.Data.Excel; + +[ResourceEntity("dlc/vircapture/timelist.json")] +public class VirCaptureTimeExcel : ExcelResource +{ + [JsonProperty("Id")] public uint Id { get; set; } + [JsonProperty("StartTime")] public string StartTime { get; set; } = ""; + [JsonProperty("EndTime")] public string EndTime { get; set; } = ""; + + public override uint GetId() => Id; + + public override void Loaded() + { + GameData.VirCaptureTimeData[Id] = this; + } +} diff --git a/Common/Data/Excel/VirCaptureTrialTimeExcel.cs b/Common/Data/Excel/VirCaptureTrialTimeExcel.cs new file mode 100644 index 0000000..bdeb804 --- /dev/null +++ b/Common/Data/Excel/VirCaptureTrialTimeExcel.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; + +namespace MikuSB.Data.Excel; + +[ResourceEntity("dlc/vircapture/trial_timelist.json")] +public class VirCaptureTrialTimeExcel : ExcelResource +{ + [JsonProperty("Id")] public uint Id { get; set; } + [JsonProperty("StartTime")] public string StartTime { get; set; } = ""; + [JsonProperty("EndTime")] public string EndTime { get; set; } = ""; + [JsonProperty("AwardTime")] public string AwardTime { get; set; } = ""; + + public override uint GetId() => Id; + + public override void Loaded() + { + GameData.VirCaptureTrialTimeData[Id] = this; + } +} diff --git a/Common/Data/GameData.cs b/Common/Data/GameData.cs index c8e7b1e..2e526ee 100644 --- a/Common/Data/GameData.cs +++ b/Common/Data/GameData.cs @@ -50,6 +50,9 @@ public static class GameData public static Dictionary GachaData { get; private set; } = []; public static Dictionary GachaProbabilityData { get; private set; } = []; public static Dictionary> GachaPoolData { get; private set; } = []; + public static Dictionary VirCaptureTimeData { get; private set; } = []; + public static Dictionary VirCaptureSeasonData { get; private set; } = []; + public static Dictionary VirCaptureTrialTimeData { get; private set; } = []; } public static class GameResourceTemplateId diff --git a/GameServer/Server/CallGS/Handlers/VirCapture/VirCapture_CheckOpenAct.cs b/GameServer/Server/CallGS/Handlers/VirCapture/VirCapture_CheckOpenAct.cs new file mode 100644 index 0000000..43d5465 --- /dev/null +++ b/GameServer/Server/CallGS/Handlers/VirCapture/VirCapture_CheckOpenAct.cs @@ -0,0 +1,164 @@ +using MikuSB.Data; +using MikuSB.Data.Excel; +using MikuSB.Database.Player; +using MikuSB.GameServer.Game.Player; +using MikuSB.Proto; +using System.Globalization; +using System.Text.Json.Nodes; + +namespace MikuSB.GameServer.Server.CallGS.Handlers.VirCapture; + +[CallGSApi("VirCapture_CheckOpenAct")] +public class VirCapture_CheckOpenAct : ICallGSHandler +{ + private const uint GroupId = 128; + private const uint ActIdSid = 1; + private const uint CurLevelSid = 3; + private const uint TrialActIdSid = 6; + private const uint SeasonActIdSid = 9; + + public async Task Handle(Connection connection, string param, ushort seqNo) + { + var now = DateTime.Now; + var act = ResolveCurrent(GameData.VirCaptureTimeData.Values, now); + if (act == null) + { + await CallGSRouter.SendScript(connection, "VirCapture_CheckOpenAct", "{\"bOpen\":false}"); + return; + } + + var player = connection.Player!; + var sync = new NtfSyncPlayer(); + + SetAttr(player, ActIdSid, act.Id, sync); + EnsureMinAttr(player, CurLevelSid, 1, sync); + + var response = new JsonObject + { + ["bOpen"] = true, + ["nId"] = act.Id, + ["nStartTime"] = ToUnixSeconds(ParseConfigTime(act.StartTime)), + ["nEndTime"] = ToUnixSeconds(ParseConfigTime(act.EndTime)) + }; + + var season = ResolveCurrent(GameData.VirCaptureSeasonData.Values, now); + if (season != null) + { + SetAttr(player, SeasonActIdSid, season.Id, sync); + response["tbSeason"] = new JsonObject + { + ["nId"] = season.Id, + ["nStartTime"] = ToUnixSeconds(ParseConfigTime(season.StartTime)), + ["nEndTime"] = ToUnixSeconds(ParseConfigTime(season.EndTime)) + }; + } + else + { + SetAttr(player, SeasonActIdSid, 0, sync); + } + + var trial = ResolveCurrent(GameData.VirCaptureTrialTimeData.Values, now); + SetAttr(player, TrialActIdSid, trial?.Id ?? 0, sync); + + await CallGSRouter.SendScript(connection, "VirCapture_CheckOpenAct", response.ToJsonString(), sync); + } + + private static T? ResolveCurrent(IEnumerable configs, DateTime now) where T : class + { + var parsed = configs + .Select(x => new + { + Config = x, + Start = ParseConfigTime(GetTimeValue(x, true)), + End = ParseConfigTime(GetTimeValue(x, false)) + }) + .Where(x => x.Start.HasValue && x.End.HasValue) + .OrderBy(x => x.Start) + .ToList(); + + var current = parsed.FirstOrDefault(x => x.Start <= now && now < x.End); + if (current != null) + return current.Config; + + var latestStarted = parsed.LastOrDefault(x => x.Start <= now); + if (latestStarted != null && latestStarted.End > latestStarted.Start) + return latestStarted.Config; + + return null; + } + + private static string? GetTimeValue(T value, bool start) where T : class + { + return value switch + { + VirCaptureTimeExcel time => start ? time.StartTime : time.EndTime, + VirCaptureSeasonExcel season => start ? season.StartTime : season.EndTime, + _ => null + }; + } + + private static DateTime? ParseConfigTime(string? raw) + { + if (string.IsNullOrWhiteSpace(raw)) + return null; + + var normalized = raw.Trim().Trim('[', ']'); + if (normalized.Length != 12) + return null; + + return DateTime.TryParseExact( + normalized, + "yyyyMMddHHmm", + CultureInfo.InvariantCulture, + DateTimeStyles.None, + out var value) + ? value + : null; + } + + private static long ToUnixSeconds(DateTime? value) + { + return value.HasValue ? new DateTimeOffset(value.Value).ToUnixTimeSeconds() : 0L; + } + + private static void EnsureMinAttr(PlayerInstance player, uint sid, uint minValue, NtfSyncPlayer sync) + { + var attr = GetOrCreateAttr(player, sid); + if (attr.Val < minValue) + { + attr.Val = minValue; + SyncAttr(player, sync, sid, attr.Val); + } + } + + private static void SetAttr(PlayerInstance player, uint sid, uint value, NtfSyncPlayer sync) + { + var attr = GetOrCreateAttr(player, sid); + if (attr.Val != value) + { + attr.Val = value; + SyncAttr(player, sync, sid, value); + } + } + + private static PlayerAttr GetOrCreateAttr(PlayerInstance player, uint sid) + { + var attr = player.Data.Attrs.FirstOrDefault(x => x.Gid == GroupId && x.Sid == sid); + if (attr != null) + return attr; + + attr = new PlayerAttr + { + Gid = GroupId, + Sid = sid + }; + player.Data.Attrs.Add(attr); + return attr; + } + + private static void SyncAttr(PlayerInstance player, NtfSyncPlayer sync, uint sid, uint value) + { + sync.Custom[player.ToPackedAttrKey(GroupId, sid)] = value; + sync.Custom[player.ToShiftedAttrKey(GroupId, sid)] = value; + } +}