mirror of
https://github.com/MikuLeaks/MikuSB.git
synced 2026-06-04 12:43:58 +00:00
Compare commits
4 Commits
v4.1
...
a50b0563be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a50b0563be | ||
|
|
b78c709f76 | ||
|
|
12094f6dd1 | ||
|
|
c3b675dc34 |
19
Common/Data/Excel/VirCaptureCaptureRegionExcel.cs
Normal file
19
Common/Data/Excel/VirCaptureCaptureRegionExcel.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace MikuSB.Data.Excel;
|
||||
|
||||
[ResourceEntity("dlc/vircapture/captureregion.json")]
|
||||
public class VirCaptureCaptureRegionExcel : ExcelResource
|
||||
{
|
||||
[JsonProperty("Id")] public uint Id { get; set; }
|
||||
[JsonProperty("StartTime")] public string StartTime { get; set; } = "";
|
||||
[JsonProperty("EndTime")] public string EndTime { get; set; } = "";
|
||||
[JsonProperty("MapId")] public uint MapId { get; set; }
|
||||
|
||||
public override uint GetId() => Id;
|
||||
|
||||
public override void Loaded()
|
||||
{
|
||||
GameData.VirCaptureCaptureRegionData[Id] = this;
|
||||
}
|
||||
}
|
||||
18
Common/Data/Excel/VirCaptureSeasonExcel.cs
Normal file
18
Common/Data/Excel/VirCaptureSeasonExcel.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
19
Common/Data/Excel/VirCaptureTimeExcel.cs
Normal file
19
Common/Data/Excel/VirCaptureTimeExcel.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
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; } = "";
|
||||
[JsonProperty("CaptureRegionId")] public List<uint> CaptureRegionId { get; set; } = [];
|
||||
|
||||
public override uint GetId() => Id;
|
||||
|
||||
public override void Loaded()
|
||||
{
|
||||
GameData.VirCaptureTimeData[Id] = this;
|
||||
}
|
||||
}
|
||||
19
Common/Data/Excel/VirCaptureTrialTimeExcel.cs
Normal file
19
Common/Data/Excel/VirCaptureTrialTimeExcel.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,10 @@ public static class GameData
|
||||
public static Dictionary<uint, GachaExcel> GachaData { get; private set; } = [];
|
||||
public static Dictionary<uint, GachaProbabilityExcel> GachaProbabilityData { get; private set; } = [];
|
||||
public static Dictionary<string, List<GachaPoolItem>> GachaPoolData { get; private set; } = [];
|
||||
public static Dictionary<uint, VirCaptureTimeExcel> VirCaptureTimeData { get; private set; } = [];
|
||||
public static Dictionary<uint, VirCaptureSeasonExcel> VirCaptureSeasonData { get; private set; } = [];
|
||||
public static Dictionary<uint, VirCaptureTrialTimeExcel> VirCaptureTrialTimeData { get; private set; } = [];
|
||||
public static Dictionary<uint, VirCaptureCaptureRegionExcel> VirCaptureCaptureRegionData { get; private set; } = [];
|
||||
}
|
||||
|
||||
public static class GameResourceTemplateId
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
using MikuSB.Database;
|
||||
using MikuSB.Proto;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MikuSB.GameServer.Server.CallGS.Handlers.VirCapture;
|
||||
|
||||
[CallGSApi("VirCaptureLevel_ChangeFlag")]
|
||||
public class VirCaptureLevel_ChangeFlag : ICallGSHandler
|
||||
{
|
||||
public async Task Handle(Connection connection, string param, ushort seqNo)
|
||||
{
|
||||
var req = JsonSerializer.Deserialize<VirCaptureChangeFlagParam>(param);
|
||||
if (req == null || req.LevelId == 0 || req.RegionId == 0)
|
||||
{
|
||||
await CallGSRouter.SendScript(connection, "VirCaptureLevel_ChangeFlag", "{\"sErr\":\"error.BadParam\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
var player = connection.Player!;
|
||||
var sync = new NtfSyncPlayer();
|
||||
VirCaptureStateHelper.SetPointState(player, (uint)req.LevelId, (uint)req.RegionId, req.Clean ? 0u : 1u, sync);
|
||||
|
||||
DatabaseHelper.SaveDatabaseType(player.Data);
|
||||
var rsp = $"{{\"nLevelID\":{req.LevelId},\"nRegionId\":{req.RegionId},\"bClean\":{req.Clean.ToString().ToLowerInvariant()}}}";
|
||||
await CallGSRouter.SendScript(connection, "VirCaptureLevel_ChangeFlag", rsp, sync);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class VirCaptureChangeFlagParam
|
||||
{
|
||||
[JsonPropertyName("nLevelID")]
|
||||
public int LevelId { get; set; }
|
||||
|
||||
[JsonPropertyName("nRegionId")]
|
||||
public int RegionId { get; set; }
|
||||
|
||||
[JsonPropertyName("bClean")]
|
||||
public bool Clean { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
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;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MikuSB.GameServer.Server.CallGS.Handlers.VirCapture;
|
||||
|
||||
[CallGSApi("VirCaptureLevel_EnterLevel")]
|
||||
public class VirCaptureLevel_EnterLevel : ICallGSHandler
|
||||
{
|
||||
private const uint GroupId = 128;
|
||||
private const uint MapDataStart = 10000;
|
||||
private const uint MaxMapCount = 3;
|
||||
private const uint MaxMapDataLen = 3000;
|
||||
private const uint OffMapId = 1;
|
||||
private const uint OffDayNight = 7;
|
||||
private const uint OffMapLevel = 8;
|
||||
private static readonly Random Random = new();
|
||||
|
||||
public async Task Handle(Connection connection, string param, ushort seqNo)
|
||||
{
|
||||
var req = JsonSerializer.Deserialize<VirCaptureEnterLevelParam>(param);
|
||||
if (req == null || req.LevelId == 0 || req.TeamId <= 0)
|
||||
{
|
||||
await CallGSRouter.SendScript(connection, "VirCaptureLevel_EnterLevel", "{\"sErr\":\"error.BadParam\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
var now = DateTime.Now;
|
||||
var act = ResolveCurrent(GameData.VirCaptureTimeData.Values, now);
|
||||
if (act == null || !act.CaptureRegionId.Contains((uint)req.LevelId))
|
||||
{
|
||||
await CallGSRouter.SendScript(connection, "VirCaptureLevel_EnterLevel", "{\"sErr\":\"ui.TxtNotOpen\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GameData.VirCaptureCaptureRegionData.TryGetValue((uint)req.LevelId, out var region))
|
||||
{
|
||||
await CallGSRouter.SendScript(connection, "VirCaptureLevel_EnterLevel", "{\"sErr\":\"error.BadParam\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
var regionStart = ParseConfigTime(region.StartTime);
|
||||
var regionEnd = ParseConfigTime(region.EndTime);
|
||||
if (!regionStart.HasValue || !regionEnd.HasValue || now < regionStart.Value || now >= regionEnd.Value)
|
||||
{
|
||||
await CallGSRouter.SendScript(connection, "VirCaptureLevel_EnterLevel", "{\"sErr\":\"ui.TxtNotOpen\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
var player = connection.Player!;
|
||||
var sync = new NtfSyncPlayer();
|
||||
EnsureMapState(player, (uint)req.LevelId, sync);
|
||||
|
||||
var rsp = $"{{\"nSeed\":{Random.Next(1, 1_000_000_000)}}}";
|
||||
await CallGSRouter.SendScript(connection, "VirCaptureLevel_EnterLevel", rsp, sync);
|
||||
}
|
||||
|
||||
private static void EnsureMapState(PlayerInstance player, uint levelId, NtfSyncPlayer sync)
|
||||
{
|
||||
var slotStart = FindOrAllocateMapSlot(player, levelId);
|
||||
if (slotStart == 0)
|
||||
return;
|
||||
|
||||
EnsureMapAttr(player, slotStart + OffMapId, levelId, sync);
|
||||
EnsureMapAttr(player, slotStart + OffDayNight, 1, sync);
|
||||
EnsureMapAttr(player, slotStart + OffMapLevel, 1, sync);
|
||||
}
|
||||
|
||||
private static uint FindOrAllocateMapSlot(PlayerInstance player, uint levelId)
|
||||
{
|
||||
uint? emptySlot = null;
|
||||
for (uint i = 0; i < MaxMapCount; i++)
|
||||
{
|
||||
var slotStart = MapDataStart + (i * MaxMapDataLen);
|
||||
var mapIdAttr = player.Data.Attrs.FirstOrDefault(x => x.Gid == GroupId && x.Sid == slotStart + OffMapId);
|
||||
if (mapIdAttr?.Val == levelId)
|
||||
return slotStart;
|
||||
|
||||
if (emptySlot == null && (mapIdAttr == null || mapIdAttr.Val == 0))
|
||||
emptySlot = slotStart;
|
||||
}
|
||||
|
||||
return emptySlot ?? 0;
|
||||
}
|
||||
|
||||
private static void EnsureMapAttr(PlayerInstance player, uint sid, uint minValue, NtfSyncPlayer sync)
|
||||
{
|
||||
var attr = player.Data.Attrs.FirstOrDefault(x => x.Gid == GroupId && x.Sid == sid);
|
||||
if (attr == null)
|
||||
{
|
||||
attr = new PlayerAttr
|
||||
{
|
||||
Gid = GroupId,
|
||||
Sid = sid,
|
||||
Val = minValue
|
||||
};
|
||||
player.Data.Attrs.Add(attr);
|
||||
SyncAttr(player, sync, sid, minValue);
|
||||
return;
|
||||
}
|
||||
|
||||
if (attr.Val < minValue)
|
||||
{
|
||||
attr.Val = minValue;
|
||||
SyncAttr(player, sync, sid, attr.Val);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private static VirCaptureTimeExcel? ResolveCurrent(IEnumerable<VirCaptureTimeExcel> configs, DateTime now)
|
||||
{
|
||||
var parsed = configs
|
||||
.Select(x => new
|
||||
{
|
||||
Config = x,
|
||||
Start = ParseConfigTime(x.StartTime),
|
||||
End = ParseConfigTime(x.EndTime)
|
||||
})
|
||||
.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 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;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class VirCaptureEnterLevelParam
|
||||
{
|
||||
[JsonPropertyName("nLevelID")]
|
||||
public int LevelId { get; set; }
|
||||
|
||||
[JsonPropertyName("nTeamID")]
|
||||
public int TeamId { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using MikuSB.Database;
|
||||
using MikuSB.Proto;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MikuSB.GameServer.Server.CallGS.Handlers.VirCapture;
|
||||
|
||||
[CallGSApi("VirCaptureLevel_SaveCapture")]
|
||||
public class VirCaptureLevel_SaveCapture : ICallGSHandler
|
||||
{
|
||||
public async Task Handle(Connection connection, string param, ushort seqNo)
|
||||
{
|
||||
var req = JsonSerializer.Deserialize<VirCaptureSaveCaptureParam>(param);
|
||||
if (req == null || req.LevelId == 0 || req.RegionId == 0)
|
||||
{
|
||||
await CallGSRouter.SendScript(connection, "VirCaptureLevel_SaveCapture", "{\"sErr\":\"error.BadParam\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
var player = connection.Player!;
|
||||
var sync = new NtfSyncPlayer();
|
||||
VirCaptureStateHelper.SetPointState(player, (uint)req.LevelId, (uint)req.RegionId, 2u, sync);
|
||||
|
||||
DatabaseHelper.SaveDatabaseType(player.Data);
|
||||
|
||||
var response = new JsonObject
|
||||
{
|
||||
["nLevelID"] = req.LevelId,
|
||||
["nRegionId"] = req.RegionId
|
||||
};
|
||||
|
||||
await CallGSRouter.SendScript(connection, "VirCaptureLevel_SaveCapture", response.ToJsonString(), sync);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class VirCaptureSaveCaptureParam
|
||||
{
|
||||
[JsonPropertyName("nLevelID")]
|
||||
public int LevelId { get; set; }
|
||||
|
||||
[JsonPropertyName("nRegionId")]
|
||||
public int RegionId { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using MikuSB.Database;
|
||||
using MikuSB.Proto;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MikuSB.GameServer.Server.CallGS.Handlers.VirCapture;
|
||||
|
||||
[CallGSApi("VirCaptureLevel_SaveFightData")]
|
||||
public class VirCaptureLevel_SaveFightData : ICallGSHandler
|
||||
{
|
||||
public async Task Handle(Connection connection, string param, ushort seqNo)
|
||||
{
|
||||
var req = JsonSerializer.Deserialize<VirCaptureSaveFightDataParam>(param);
|
||||
if (req == null || req.LevelId == 0 || req.RegionId == 0)
|
||||
{
|
||||
await CallGSRouter.SendScript(connection, "VirCaptureLevel_SaveFightData", "{\"sErr\":\"error.BadParam\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
var player = connection.Player!;
|
||||
var sync = new NtfSyncPlayer();
|
||||
VirCaptureStateHelper.SetPointState(player, (uint)req.LevelId, (uint)req.RegionId, 2u, sync);
|
||||
|
||||
DatabaseHelper.SaveDatabaseType(player.Data);
|
||||
|
||||
var response = new JsonObject
|
||||
{
|
||||
["nLevelID"] = req.LevelId,
|
||||
["nRegionId"] = req.RegionId,
|
||||
["tbRewards"] = new JsonArray()
|
||||
};
|
||||
|
||||
await CallGSRouter.SendScript(connection, "VirCaptureLevel_SaveFightData", response.ToJsonString(), sync);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class VirCaptureSaveFightDataParam
|
||||
{
|
||||
[JsonPropertyName("nLevelID")]
|
||||
public int LevelId { get; set; }
|
||||
|
||||
[JsonPropertyName("nRegionId")]
|
||||
public int RegionId { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using MikuSB.Database;
|
||||
using MikuSB.Proto;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MikuSB.GameServer.Server.CallGS.Handlers.VirCapture;
|
||||
|
||||
[CallGSApi("VirCaptureLevel_SavePos")]
|
||||
public class VirCaptureLevel_SavePos : ICallGSHandler
|
||||
{
|
||||
public async Task Handle(Connection connection, string param, ushort seqNo)
|
||||
{
|
||||
var req = JsonSerializer.Deserialize<VirCaptureSavePosParam>(param);
|
||||
if (req == null || req.LevelId == 0)
|
||||
{
|
||||
await CallGSRouter.SendScript(connection, "VirCaptureLevel_SavePos", "{\"sErr\":\"error.BadParam\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
var player = connection.Player!;
|
||||
var sync = new NtfSyncPlayer();
|
||||
VirCaptureStateHelper.SetSignedMapOffset(player, (uint)req.LevelId, VirCaptureStateHelper.OffPosX, req.PosX, sync);
|
||||
VirCaptureStateHelper.SetSignedMapOffset(player, (uint)req.LevelId, VirCaptureStateHelper.OffPosY, req.PosY, sync);
|
||||
VirCaptureStateHelper.SetSignedMapOffset(player, (uint)req.LevelId, VirCaptureStateHelper.OffPosZ, req.PosZ, sync);
|
||||
VirCaptureStateHelper.SetSignedMapOffset(player, (uint)req.LevelId, VirCaptureStateHelper.OffToward, req.Toward, sync);
|
||||
|
||||
DatabaseHelper.SaveDatabaseType(player.Data);
|
||||
await CallGSRouter.SendScript(connection, "VirCaptureLevel_SavePos", "{}", sync);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class VirCaptureSavePosParam
|
||||
{
|
||||
[JsonPropertyName("nLevelID")]
|
||||
public int LevelId { get; set; }
|
||||
|
||||
[JsonPropertyName("nPosX")]
|
||||
public int PosX { get; set; }
|
||||
|
||||
[JsonPropertyName("nPosY")]
|
||||
public int PosY { get; set; }
|
||||
|
||||
[JsonPropertyName("nPosZ")]
|
||||
public int PosZ { get; set; }
|
||||
|
||||
[JsonPropertyName("nToward")]
|
||||
public int Toward { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
using MikuSB.Database.Player;
|
||||
using MikuSB.GameServer.Game.Player;
|
||||
using MikuSB.Proto;
|
||||
|
||||
namespace MikuSB.GameServer.Server.CallGS.Handlers.VirCapture;
|
||||
|
||||
internal static class VirCaptureStateHelper
|
||||
{
|
||||
public const uint GroupId = 128;
|
||||
public const uint MapDataStart = 10000;
|
||||
public const uint MapDataEnd = 19000;
|
||||
public const uint MaxMapCount = 3;
|
||||
public const uint MaxMapDataLen = 3000;
|
||||
public const uint MaxPatrolPoint = 500;
|
||||
public const uint MaxOtherPoint = 2500;
|
||||
public const uint MinMaterialId = 50000;
|
||||
public const uint MaxMaterialId = 51500;
|
||||
|
||||
public const uint OffMapId = 1;
|
||||
public const uint OffTurnNum = 2;
|
||||
public const uint OffPosX = 3;
|
||||
public const uint OffPosY = 4;
|
||||
public const uint OffPosZ = 5;
|
||||
public const uint OffToward = 6;
|
||||
public const uint OffDayNight = 7;
|
||||
public const uint OffMapLevel = 8;
|
||||
public const uint OffPatrolStart = 51;
|
||||
public const uint OffPatrolEnd = 1000;
|
||||
public const uint OffOtherStart = 1001;
|
||||
public const uint OffOtherEnd = 1500;
|
||||
public const uint OffMaterialStart = 1501;
|
||||
public const uint OffMaterialEnd = 3000;
|
||||
|
||||
public static uint FindOrAllocateMapSlot(PlayerInstance player, uint levelId)
|
||||
{
|
||||
uint? emptySlot = null;
|
||||
for (uint i = 0; i < MaxMapCount; i++)
|
||||
{
|
||||
var slotStart = MapDataStart + (i * MaxMapDataLen);
|
||||
var mapIdAttr = player.Data.Attrs.FirstOrDefault(x => x.Gid == GroupId && x.Sid == slotStart + OffMapId);
|
||||
if (mapIdAttr?.Val == levelId)
|
||||
return slotStart;
|
||||
|
||||
if (emptySlot == null && (mapIdAttr == null || mapIdAttr.Val == 0))
|
||||
emptySlot = slotStart;
|
||||
}
|
||||
|
||||
return emptySlot ?? 0;
|
||||
}
|
||||
|
||||
public static void EnsureBaseMapState(PlayerInstance player, uint levelId, NtfSyncPlayer sync)
|
||||
{
|
||||
var slotStart = FindOrAllocateMapSlot(player, levelId);
|
||||
if (slotStart == 0)
|
||||
return;
|
||||
|
||||
EnsureUnsignedAttr(player, slotStart + OffMapId, levelId, sync);
|
||||
EnsureUnsignedAttr(player, slotStart + OffDayNight, 1, sync);
|
||||
EnsureUnsignedAttr(player, slotStart + OffMapLevel, 1, sync);
|
||||
}
|
||||
|
||||
public static void SetSignedMapOffset(PlayerInstance player, uint levelId, uint offset, int value, NtfSyncPlayer sync)
|
||||
{
|
||||
var slotStart = FindOrAllocateMapSlot(player, levelId);
|
||||
if (slotStart == 0)
|
||||
return;
|
||||
|
||||
EnsureBaseMapState(player, levelId, sync);
|
||||
SetUnsignedAttr(player, slotStart + offset, unchecked((uint)value), sync);
|
||||
}
|
||||
|
||||
public static void SetPointState(PlayerInstance player, uint levelId, uint pointId, uint value, NtfSyncPlayer sync)
|
||||
{
|
||||
var slotStart = FindOrAllocateMapSlot(player, levelId);
|
||||
if (slotStart == 0 || pointId == 0)
|
||||
return;
|
||||
|
||||
EnsureBaseMapState(player, levelId, sync);
|
||||
|
||||
if (pointId <= MaxPatrolPoint)
|
||||
{
|
||||
var sid = slotStart + (OffPatrolStart - 1) + pointId;
|
||||
SetUnsignedAttr(player, sid, value, sync);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pointId <= MaxOtherPoint)
|
||||
{
|
||||
var relative = pointId - MaxPatrolPoint;
|
||||
var sid = slotStart + (uint)Math.Floor(relative / 30d) + OffOtherStart;
|
||||
if (sid > slotStart + OffOtherEnd)
|
||||
return;
|
||||
|
||||
var bit = (int)(relative % 30);
|
||||
var attr = GetOrCreateAttr(player, sid);
|
||||
var next = value > 0
|
||||
? attr.Val | (1u << bit)
|
||||
: attr.Val & ~(1u << bit);
|
||||
if (next != attr.Val)
|
||||
{
|
||||
attr.Val = next;
|
||||
SyncAttr(player, sync, sid, next);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (pointId > MinMaterialId && pointId <= MaxMaterialId)
|
||||
{
|
||||
var sid = slotStart + (OffMaterialStart - 1) + (pointId - MinMaterialId);
|
||||
if (sid >= slotStart + OffMaterialEnd)
|
||||
return;
|
||||
|
||||
SetUnsignedAttr(player, sid, value, sync);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EnsureUnsignedAttr(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);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetUnsignedAttr(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;
|
||||
}
|
||||
}
|
||||
@@ -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<T>(IEnumerable<T> 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>(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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user