mirror of
https://github.com/EpinelPS/EpinelPS.git
synced 2025-12-12 15:04:36 +01:00
新增功能:实现包含简易模式特性的模拟房间功能 (#66)
feat: Implement SimRoom functionality with Simple Mode features - Added SimRoomHelper class to manage SimRoom events and logic. - Implemented SimpleModeSelectBuff handler for buff selection in Simple Mode. - Implemented SimpleModeSetSkipOption to enable/disable skip options. - Implemented SimpleModeSkipAll to handle skipping all Simple Mode stages and reward retrieval. - Implemented SimpleModeSkipBuffSelection for skipping buff selection. - Implemented SimpleModeStart to initiate Simple Mode with event handling. - Updated SimRoom data models to include buffs, legacy buffs, and event tracking. - Updated GameData add SimRoom data tables -Added JsonStaticDataReplenish add SimRoom data models - Enhanced User model to manage weekly reset logic and retain legacy buffs. - Added DateTimeHelper utility for managing time zone specific date calculations. - Updated game configuration for static data and resource URLs.
This commit is contained in:
@@ -291,6 +291,24 @@ namespace EpinelPS.Data
|
|||||||
[LoadRecord("DailyEventTable.json", "Id")]
|
[LoadRecord("DailyEventTable.json", "Id")]
|
||||||
public readonly Dictionary<int, DailyEventRecord> DailyEventTable = [];
|
public readonly Dictionary<int, DailyEventRecord> DailyEventTable = [];
|
||||||
|
|
||||||
|
// SimulationRoom Data Tables
|
||||||
|
[LoadRecord("SimulationRoomChapterTable.json", "Id")]
|
||||||
|
public readonly Dictionary<int, SimulationRoomChapterRecord> SimulationRoomChapterTable = [];
|
||||||
|
[LoadRecord("SimulationRoomStageLocationTable.json", "Id")]
|
||||||
|
public readonly Dictionary<int, SimulationRoomStageLocationRecord> SimulationRoomStageLocationTable = [];
|
||||||
|
[LoadRecord("SimulationRoomSelectionEventTable.json", "Id")]
|
||||||
|
public readonly Dictionary<int, SimulationRoomSelectionEventRecord> SimulationRoomSelectionEventTable = [];
|
||||||
|
[LoadRecord("SimulationRoomSelectionGroupTable.json", "Id")]
|
||||||
|
public readonly Dictionary<int, SimulationRoomSelectionGroupRecord> SimulationRoomSelectionGroupTable = [];
|
||||||
|
[LoadRecord("SimulationRoomBattleEventTable.json", "Id")]
|
||||||
|
public readonly Dictionary<int, SimulationRoomBattleEventRecord> SimulationRoomBattleEventTable = [];
|
||||||
|
[LoadRecord("SimulationRoomLevelScalingTable.json", "Id")]
|
||||||
|
public readonly Dictionary<int, SimulationRoomLevelScalingRecord> SimulationRoomLevelScalingTable = [];
|
||||||
|
[LoadRecord("SimulationRoomBuffPreviewTable.json", "Id")]
|
||||||
|
public readonly Dictionary<int, SimulationRoomBuffPreviewRecord> SimulationRoomBuffPreviewTable = [];
|
||||||
|
[LoadRecord("SimulationRoomBuffTable.json", "Id")]
|
||||||
|
public readonly Dictionary<int, SimulationRoomBuffRecord> SimulationRoomBuffTable = [];
|
||||||
|
|
||||||
static async Task<GameData> BuildAsync()
|
static async Task<GameData> BuildAsync()
|
||||||
{
|
{
|
||||||
await Load();
|
await Load();
|
||||||
|
|||||||
155
EpinelPS/Data/JsonStaticDataReplenish.cs
Normal file
155
EpinelPS/Data/JsonStaticDataReplenish.cs
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
using MemoryPack;
|
||||||
|
|
||||||
|
namespace EpinelPS.Data;
|
||||||
|
|
||||||
|
[MemoryPackable]
|
||||||
|
public partial class SimulationRoomStageLocationRecord
|
||||||
|
{
|
||||||
|
public int Id;
|
||||||
|
public int ScheduleGroupId;
|
||||||
|
public int ChapterId;
|
||||||
|
public int StageGroupId;
|
||||||
|
public int StageEssentialValue;
|
||||||
|
public int EventSelectionValue;
|
||||||
|
public SimulationRoomLocation Location;
|
||||||
|
public int Weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MemoryPackable]
|
||||||
|
public partial class SimulationRoomBattleEventRecord
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public SimulationRoomEvent EventType { get; set; }
|
||||||
|
public SimulationRoomScalingType ScalingType { get; set; }
|
||||||
|
public SimulationRoomConditionType DifficultyConditionType { get; set; }
|
||||||
|
public int DifficultyConditionValue { get; set; }
|
||||||
|
public SimulationRoomConditionType ChapterConditionType { get; set; }
|
||||||
|
public int ChapterConditionValue { get; set; }
|
||||||
|
public int Weight { get; set; }
|
||||||
|
public bool SpotAutocontrol { get; set; }
|
||||||
|
public int MonsterStageLv { get; set; }
|
||||||
|
public int DynamicObjectStageLv { get; set; }
|
||||||
|
public int StandardBattlePower { get; set; }
|
||||||
|
public int StageStatIncreaseGroupId { get; set; }
|
||||||
|
public bool IsUseQuickBattle { get; set; }
|
||||||
|
public int SpotId { get; set; }
|
||||||
|
public bool UseOcMode { get; set; }
|
||||||
|
public int UseSeasonId { get; set; }
|
||||||
|
public int BattleEventGroup { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[MemoryPackable]
|
||||||
|
public partial class SimulationRoomBuffPreviewRecord
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public SimulationRoomEvent EventType { get; set; }
|
||||||
|
public PreviewType PreviewType { get; set; }
|
||||||
|
public string? PreviewTarget { get; set; }
|
||||||
|
public int Weight { get; set; }
|
||||||
|
public string? DescriptionLocalkey { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[MemoryPackable]
|
||||||
|
public partial class SimulationRoomBuffRecord
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public int GroupId { get; set; }
|
||||||
|
public SimulationRoomBuffMainTarget MainTarget { get; set; }
|
||||||
|
public List<SimulationRoomBuffSubTarget>? SubTarget { get; set; }
|
||||||
|
public SimulationRoomBuffGrade Grade { get; set; }
|
||||||
|
public int Weight { get; set; }
|
||||||
|
public SimulationRoomBubbleType BubbleType { get; set; }
|
||||||
|
public string? NameLocalkey { get; set; }
|
||||||
|
public string? DescriptionLocalkey { get; set; }
|
||||||
|
public List<string>? ParameterLocalkey { get; set; }
|
||||||
|
public string? ResourceId { get; set; }
|
||||||
|
public SimulationRoomBuffFunctionType FunctionType { get; set; }
|
||||||
|
public List<SimulationRoomBuffValueData>? BuffValue { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[MemoryPackable]
|
||||||
|
public partial class SimulationRoomSelectionEventRecord
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public SimulationRoomEvent EventType { get; set; }
|
||||||
|
public SimulationRoomConditionType ChapterConditionType { get; set; }
|
||||||
|
public int ChapterConditionValue { get; set; }
|
||||||
|
public int SelectionGroupId { get; set; }
|
||||||
|
public int SelectionValue { get; set; }
|
||||||
|
public string? NameLocalkey { get; set; }
|
||||||
|
public string? DescriptionLocalkey { get; set; }
|
||||||
|
public int Weight { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[MemoryPackable]
|
||||||
|
public partial class SimulationRoomBuffValueData
|
||||||
|
{
|
||||||
|
public int FunctionValueLevel { get; set; }
|
||||||
|
public int BattlePowerLevel { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SimulationRoomScalingType
|
||||||
|
{
|
||||||
|
DataRef = 0,
|
||||||
|
None = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SimulationRoomConditionType
|
||||||
|
{
|
||||||
|
Range = 0, // 0x0
|
||||||
|
Select = 1, // 0x0
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PreviewType
|
||||||
|
{
|
||||||
|
MainTarget = 0, // 0x0
|
||||||
|
Bubble = 1, // 0x0
|
||||||
|
Grade = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SimulationRoomBuffMainTarget
|
||||||
|
{
|
||||||
|
Shoot = 0, // 0x0
|
||||||
|
Attack = 1, // 0x0
|
||||||
|
Survive = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SimulationRoomBuffSubTarget
|
||||||
|
{
|
||||||
|
AR = 0,
|
||||||
|
RL = 1,
|
||||||
|
SR = 2,
|
||||||
|
MG = 3,
|
||||||
|
SG = 4,
|
||||||
|
SMG = 5,
|
||||||
|
ELYSION = 6,
|
||||||
|
MISSILIS = 7,
|
||||||
|
TETRA = 8,
|
||||||
|
PILGRIM = 9,
|
||||||
|
Attacker = 10,
|
||||||
|
Defender = 11,
|
||||||
|
Supporter = 12,
|
||||||
|
ALL = 13
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SimulationRoomBuffGrade
|
||||||
|
{
|
||||||
|
R = 0,
|
||||||
|
SR = 1,
|
||||||
|
SSR = 2,
|
||||||
|
EPIC = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SimulationRoomBubbleType
|
||||||
|
{
|
||||||
|
TypeA = 0,
|
||||||
|
TypeB = 1,
|
||||||
|
TypeC = 2,
|
||||||
|
TypeD = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SimulationRoomBuffFunctionType
|
||||||
|
{
|
||||||
|
Function = 0,
|
||||||
|
HealAfterBattle = 1
|
||||||
|
}
|
||||||
61
EpinelPS/LobbyServer/Simroom/ClearBattle.cs
Normal file
61
EpinelPS/LobbyServer/Simroom/ClearBattle.cs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
using EpinelPS.Database;
|
||||||
|
using EpinelPS.Utils;
|
||||||
|
using log4net;
|
||||||
|
|
||||||
|
namespace EpinelPS.LobbyServer.Simroom
|
||||||
|
{
|
||||||
|
[PacketPath("/simroom/clearbattle")]
|
||||||
|
public class ClearBattle : LobbyMsgHandler
|
||||||
|
{
|
||||||
|
private static readonly ILog log = LogManager.GetLogger(typeof(ClearBattle));
|
||||||
|
|
||||||
|
protected override async Task HandleAsync()
|
||||||
|
{
|
||||||
|
// {"location":{"chapter":3,"stage":3,"order":2},"event":111011143,"teamNumber":1,"antiCheatAdditionalInfo":{"clientLocalTime":"638993283799771900"}}
|
||||||
|
ReqClearSimRoomBattle req = await ReadData<ReqClearSimRoomBattle>();
|
||||||
|
User user = GetUser();
|
||||||
|
|
||||||
|
ResClearSimRoomBattle response = new()
|
||||||
|
{
|
||||||
|
Result = SimRoomResult.Success
|
||||||
|
};
|
||||||
|
|
||||||
|
// OverclockOptionChangedHps
|
||||||
|
|
||||||
|
// Teams
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var team = SimRoomHelper.GetTeamData(user, req.TeamNumber, [.. req.RemainingHps]);
|
||||||
|
if (team is not null) response.Teams.Add(team);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.Error($"ClearBattle Response Team Exception :{e.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
SimRoomHelper.UpdateUserRemainingHps(user, [.. req.RemainingHps], req.TeamNumber);
|
||||||
|
|
||||||
|
if (req.BattleResult == 1)
|
||||||
|
{
|
||||||
|
// BuffOptions
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var buffOptions = SimRoomHelper.GetBuffOptions(user, req.Location);
|
||||||
|
if (buffOptions is not null && buffOptions.Count > 0)
|
||||||
|
{
|
||||||
|
response.BuffOptions.AddRange(buffOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.Error($"ClearBattle Response BuffOptions Exception :{e.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonDb.Save();
|
||||||
|
await WriteDataAsync(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
56
EpinelPS/LobbyServer/Simroom/ClearBattleFast.cs
Normal file
56
EpinelPS/LobbyServer/Simroom/ClearBattleFast.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
using EpinelPS.Database;
|
||||||
|
using EpinelPS.Utils;
|
||||||
|
using log4net;
|
||||||
|
|
||||||
|
namespace EpinelPS.LobbyServer.Simroom
|
||||||
|
{
|
||||||
|
[PacketPath("/simroom/fastclearbattle")]
|
||||||
|
public class ClearBattleFast : LobbyMsgHandler
|
||||||
|
{
|
||||||
|
private static readonly ILog log = LogManager.GetLogger(typeof(ClearBattle));
|
||||||
|
|
||||||
|
protected override async Task HandleAsync()
|
||||||
|
{
|
||||||
|
// {"location":{"chapter":3,"stage":3,"order":2},"event":111011143,"teamNumber":1,"antiCheatAdditionalInfo":{"clientLocalTime":"638993283799771900"}}
|
||||||
|
ReqFastClearSimRoomBattle req = await ReadData<ReqFastClearSimRoomBattle>();
|
||||||
|
User user = GetUser();
|
||||||
|
|
||||||
|
ResFastClearSimRoomBattle response = new()
|
||||||
|
{
|
||||||
|
Result = SimRoomResult.Success
|
||||||
|
};
|
||||||
|
|
||||||
|
// OverclockOptionChangedHps
|
||||||
|
|
||||||
|
// Teams
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var team = SimRoomHelper.GetTeamData(user, req.TeamNumber, null);
|
||||||
|
if (team is not null) response.Teams.Add(team);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.Error($"ClearBattleFast Response Team Exception :{e.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
SimRoomHelper.UpdateUserRemainingHps(user, teamNumber: req.TeamNumber);
|
||||||
|
|
||||||
|
// BuffOptions
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var buffOptions = SimRoomHelper.GetBuffOptions(user, req.Location);
|
||||||
|
if (buffOptions is not null && buffOptions.Count > 0)
|
||||||
|
{
|
||||||
|
response.BuffOptions.AddRange(buffOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.Error($"ClearBattleFast Response BuffOptions Exception: {e.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonDb.Save();
|
||||||
|
await WriteDataAsync(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
EpinelPS/LobbyServer/Simroom/EnterBattle.cs
Normal file
20
EpinelPS/LobbyServer/Simroom/EnterBattle.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using EpinelPS.Utils;
|
||||||
|
|
||||||
|
namespace EpinelPS.LobbyServer.Simroom
|
||||||
|
{
|
||||||
|
[PacketPath("/simroom/enterbattle")]
|
||||||
|
public class EnterBattle : LobbyMsgHandler
|
||||||
|
{
|
||||||
|
protected override async Task HandleAsync()
|
||||||
|
{
|
||||||
|
await ReadData<ReqEnterSimRoomBattle>();
|
||||||
|
|
||||||
|
ResEnterSimRoomBattle response = new()
|
||||||
|
{
|
||||||
|
Result = SimRoomResult.Success
|
||||||
|
};
|
||||||
|
|
||||||
|
await WriteDataAsync(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using Google.Protobuf.WellKnownTypes;
|
using Google.Protobuf.WellKnownTypes;
|
||||||
using EpinelPS.Utils;
|
using EpinelPS.Utils;
|
||||||
|
using EpinelPS.Data;
|
||||||
|
|
||||||
namespace EpinelPS.LobbyServer.Simroom
|
namespace EpinelPS.LobbyServer.Simroom
|
||||||
{
|
{
|
||||||
@@ -8,63 +9,192 @@ namespace EpinelPS.LobbyServer.Simroom
|
|||||||
{
|
{
|
||||||
protected override async Task HandleAsync()
|
protected override async Task HandleAsync()
|
||||||
{
|
{
|
||||||
ReqGetSimRoom req = await ReadData<ReqGetSimRoom>();
|
await ReadData<ReqGetSimRoom>();
|
||||||
User user = GetUser();
|
User user = GetUser();
|
||||||
|
|
||||||
|
// ResGetSimRoom Fields
|
||||||
|
// SimRoomStatus Status
|
||||||
|
// int CurrentDifficulty
|
||||||
|
// long NextRenewAt
|
||||||
|
// RepeatedField<NetSimRoomChapterInfo> ClearInfos
|
||||||
|
// RepeatedField<NetSimRoomEvent> Events
|
||||||
|
// RepeatedField<NetSimRoomCharacterHp> RemainingHps
|
||||||
|
// RepeatedField<int> Buffs
|
||||||
|
// RepeatedField<int> LegacyBuffs
|
||||||
|
// RepeatedField<int> OverclockOptionList
|
||||||
|
// NetSimRoomOverclockData OverclockData
|
||||||
|
// Timestamp NextLegacyBuffResetDate
|
||||||
|
// NetSimRoomSimpleModeBuffSelectionInfo NextSimpleModeBuffSelectionInfo
|
||||||
|
// NetSimRoomChapterInfo LastPlayedChapter
|
||||||
|
// bool IsSimpleModeSkipEnabled
|
||||||
|
|
||||||
|
|
||||||
|
var CurrentDifficulty = user.ResetableData.SimRoomData.CurrentDifficulty;
|
||||||
|
var currentChapter = user.ResetableData.SimRoomData.CurrentChapter;
|
||||||
|
|
||||||
ResGetSimRoom response = new()
|
ResGetSimRoom response = new()
|
||||||
{
|
{
|
||||||
OverclockData = new NetSimRoomOverclockData
|
|
||||||
{
|
|
||||||
CurrentSeasonData = new NetSimRoomOverclockSeasonData
|
|
||||||
{
|
|
||||||
SeasonStartDate = Timestamp.FromDateTimeOffset(DateTime.UtcNow),
|
|
||||||
SeasonEndDate = Timestamp.FromDateTimeOffset(DateTime.UtcNow.AddDays(7)),
|
|
||||||
IsSeasonOpen = true,
|
|
||||||
Season = 1,
|
|
||||||
SubSeason = 1,
|
|
||||||
SeasonWeekCount = 1
|
|
||||||
},
|
|
||||||
|
|
||||||
CurrentSeasonHighScore = new NetSimRoomOverclockHighScoreData
|
|
||||||
{
|
|
||||||
CreatedAt = Timestamp.FromDateTimeOffset(DateTime.UtcNow),
|
|
||||||
OptionLevel = 1,
|
|
||||||
Season = 1,
|
|
||||||
SubSeason = 1,
|
|
||||||
OptionList = {}
|
|
||||||
},
|
|
||||||
|
|
||||||
CurrentSubSeasonHighScore = new NetSimRoomOverclockHighScoreData
|
|
||||||
{
|
|
||||||
CreatedAt = Timestamp.FromDateTimeOffset(DateTime.UtcNow),
|
|
||||||
OptionLevel = 1,
|
|
||||||
Season = 1,
|
|
||||||
SubSeason = 1,
|
|
||||||
OptionList = {}
|
|
||||||
},
|
|
||||||
|
|
||||||
LatestOption = new NetSimRoomOverclockOptionSettingData
|
|
||||||
{
|
|
||||||
Season = 1,
|
|
||||||
OptionList = {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
Status = SimRoomStatus.Ready,
|
Status = SimRoomStatus.Ready,
|
||||||
CurrentDifficulty = 1,
|
CurrentDifficulty = CurrentDifficulty,
|
||||||
NextRenewAt = DateTime.UtcNow.AddDays(7).Ticks,
|
// NextRenewAt: Resets at 2 AM daily
|
||||||
NextLegacyBuffResetDate = Timestamp.FromDateTimeOffset(DateTime.UtcNow.AddDays(7))
|
NextRenewAt = DateTimeHelper.GetNextDayAtTime("China Standard Time", 2).Ticks,
|
||||||
|
// NextLegacyBuffResetDate: Resets at 2 AM every Tuesday
|
||||||
|
NextLegacyBuffResetDate = DateTimeHelper.GetNextWeekdayAtTime("China Standard Time", DayOfWeek.Tuesday, 2).ToTimestamp(),
|
||||||
|
IsSimpleModeSkipEnabled = user.ResetableData.SimRoomData.IsSimpleModeSkipEnabled,
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// LegacyBuffs
|
||||||
|
response.LegacyBuffs.AddRange(user.ResetableData.SimRoomData.LegacyBuffs);
|
||||||
|
|
||||||
|
// OverclockData
|
||||||
|
response.OverclockData = GetOverclockData(user: user);
|
||||||
|
|
||||||
|
// ClearInfos
|
||||||
|
response.ClearInfos.AddRange(GetClearInfos(user));
|
||||||
|
|
||||||
|
// OverclockOptionList
|
||||||
|
// response.OverclockOptionList.Add([]);
|
||||||
|
|
||||||
|
// check if user is in sim room
|
||||||
if (user.ResetableData.SimRoomData.Entered)
|
if (user.ResetableData.SimRoomData.Entered)
|
||||||
{
|
{
|
||||||
response.Status = SimRoomStatus.Progress;
|
response.Status = SimRoomStatus.Progress;
|
||||||
|
response.Events.AddRange(SimRoomHelper.GetSimRoomEvents(user));
|
||||||
response.CurrentDifficulty = user.ResetableData.SimRoomData.CurrentDifficulty;
|
|
||||||
|
// TODO: Get RemainingHps
|
||||||
|
response.RemainingHps.AddRange(GetCharacterHp(user));
|
||||||
|
|
||||||
|
response.LastPlayedChapter = new NetSimRoomChapterInfo()
|
||||||
|
{
|
||||||
|
Chapter = currentChapter,
|
||||||
|
Difficulty = CurrentDifficulty,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Buffs = Buffs + LegacyBuffs
|
||||||
|
response.Buffs.AddRange(user.ResetableData.SimRoomData.Buffs);
|
||||||
|
response.Buffs.AddRange(user.ResetableData.SimRoomData.LegacyBuffs);
|
||||||
|
|
||||||
|
// response.NextSimpleModeBuffSelectionInfo = new()
|
||||||
|
// {
|
||||||
|
// RemainingBuffSelectCount = 8 - response.Buffs.Count,
|
||||||
|
// BuffOptions = { user.ResetableData.SimRoomData.Buffs }
|
||||||
|
// };
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
await WriteDataAsync(response);
|
await WriteDataAsync(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get clear infos
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user"></param>
|
||||||
|
/// <returns>List of cleared chapters</returns>
|
||||||
|
private static List<NetSimRoomChapterInfo> GetClearInfos(User user)
|
||||||
|
{
|
||||||
|
List<NetSimRoomChapterInfo> clearInfos = [];
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var receivedRewards = user.ResetableData.SimRoomData.ReceivedRewardChapters;
|
||||||
|
if (receivedRewards.Count > 0)
|
||||||
|
{
|
||||||
|
// Get the last received reward chapter
|
||||||
|
var lastReceivedReward = receivedRewards.OrderBy(x => x.Difficulty).ThenBy(x => x.Chapter).LastOrDefault();
|
||||||
|
if (lastReceivedReward is not null)
|
||||||
|
{
|
||||||
|
var CurrentDifficulty = lastReceivedReward.Difficulty;
|
||||||
|
var CurrentChapter = lastReceivedReward.Chapter;
|
||||||
|
|
||||||
|
// Get all chapters where difficulty is less than or equal to current difficulty
|
||||||
|
var ChapterRecords = GameData.Instance.SimulationRoomChapterTable.Values.Where(x => x.DifficultyId <= CurrentDifficulty).ToList();
|
||||||
|
|
||||||
|
foreach (var chapterRecord in ChapterRecords)
|
||||||
|
{
|
||||||
|
// check if chapter is less than or equal to current chapter
|
||||||
|
if (chapterRecord.DifficultyId == CurrentDifficulty && chapterRecord.Chapter <= CurrentChapter)
|
||||||
|
{
|
||||||
|
clearInfos.Add(new NetSimRoomChapterInfo()
|
||||||
|
{
|
||||||
|
Chapter = chapterRecord.Chapter,
|
||||||
|
Difficulty = chapterRecord.DifficultyId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// check if difficulty is less than current difficulty
|
||||||
|
else if (chapterRecord.DifficultyId < CurrentDifficulty)
|
||||||
|
{
|
||||||
|
clearInfos.Add(new NetSimRoomChapterInfo()
|
||||||
|
{
|
||||||
|
Chapter = chapterRecord.Chapter,
|
||||||
|
Difficulty = chapterRecord.DifficultyId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logging.WriteLine($"Get ClearInfos Exception: {e.Message}", LogType.Error);
|
||||||
|
}
|
||||||
|
return clearInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<NetSimRoomCharacterHp> GetCharacterHp(User user)
|
||||||
|
{
|
||||||
|
List<NetSimRoomCharacterHp> hps = [];
|
||||||
|
if (user.UserTeams.TryGetValue((int)TeamType.SimulationRoom, out var userTeamData))
|
||||||
|
{
|
||||||
|
if (userTeamData.Teams.Count > 0 && userTeamData.Teams[0].Slots.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var slot in userTeamData.Teams[0].Slots)
|
||||||
|
{
|
||||||
|
hps.Add(new() { Csn = slot.Value, Hp = 100000 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hps;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NetSimRoomOverclockData GetOverclockData(User user)
|
||||||
|
{
|
||||||
|
return new NetSimRoomOverclockData
|
||||||
|
{
|
||||||
|
CurrentSeasonData = new NetSimRoomOverclockSeasonData
|
||||||
|
{
|
||||||
|
SeasonStartDate = Timestamp.FromDateTimeOffset(DateTime.UtcNow.Date.AddDays(-1)),
|
||||||
|
SeasonEndDate = Timestamp.FromDateTimeOffset(DateTime.UtcNow.Date.AddDays(7)),
|
||||||
|
IsSeasonOpen = true,
|
||||||
|
Season = 1,
|
||||||
|
SubSeason = 1,
|
||||||
|
SeasonWeekCount = 1
|
||||||
|
},
|
||||||
|
|
||||||
|
CurrentSeasonHighScore = new NetSimRoomOverclockHighScoreData
|
||||||
|
{
|
||||||
|
CreatedAt = Timestamp.FromDateTimeOffset(DateTime.UtcNow.Date.AddDays(-1)),
|
||||||
|
OptionLevel = 1,
|
||||||
|
Season = 1,
|
||||||
|
SubSeason = 1,
|
||||||
|
OptionList = { 1 }
|
||||||
|
},
|
||||||
|
|
||||||
|
CurrentSubSeasonHighScore = new NetSimRoomOverclockHighScoreData
|
||||||
|
{
|
||||||
|
CreatedAt = Timestamp.FromDateTimeOffset(DateTime.UtcNow.Date.AddDays(-1)),
|
||||||
|
OptionLevel = 1,
|
||||||
|
Season = 1,
|
||||||
|
SubSeason = 1,
|
||||||
|
OptionList = { 1 }
|
||||||
|
},
|
||||||
|
|
||||||
|
LatestOption = new NetSimRoomOverclockOptionSettingData
|
||||||
|
{
|
||||||
|
Season = 1,
|
||||||
|
OptionList = { 1 }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
42
EpinelPS/LobbyServer/Simroom/ProceedBuffFunction.cs
Normal file
42
EpinelPS/LobbyServer/Simroom/ProceedBuffFunction.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using EpinelPS.Database;
|
||||||
|
using EpinelPS.Utils;
|
||||||
|
|
||||||
|
namespace EpinelPS.LobbyServer.Simroom
|
||||||
|
{
|
||||||
|
[PacketPath("/simroom/proceedbufffunction")]
|
||||||
|
public class ProceedBuffFunction : LobbyMsgHandler
|
||||||
|
{
|
||||||
|
protected override async Task HandleAsync()
|
||||||
|
{
|
||||||
|
// { "location": { "chapter": 3, "stage": 6, "order": 1 }, "event": 22116, "selectionNumber": 2, "selectionGroupElementId": 221162, "buffToDelete": 2030608 }
|
||||||
|
ReqProceedSimRoomBuffFunction req = await ReadData<ReqProceedSimRoomBuffFunction>();
|
||||||
|
// ReqProceedSimRoomBuffFunction Field NetSimRoomEventLocationInfo location, int event, int selectionNumber, int selectionGroupElementId, int buffToDelete
|
||||||
|
User user = GetUser();
|
||||||
|
|
||||||
|
// ReqProceedSimRoomBuffFunction Field SimRoomResult Result, RepeatedField<int> AcquiredBuff, RepeatedField<int> DeletedBuff
|
||||||
|
ResProceedSimRoomBuffFunction response = new()
|
||||||
|
{
|
||||||
|
Result = SimRoomResult.Success
|
||||||
|
};
|
||||||
|
if (req.BuffToDelete > 0)
|
||||||
|
{
|
||||||
|
response.DeletedBuff.Add(req.BuffToDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update
|
||||||
|
var location = req.Location;
|
||||||
|
// Check
|
||||||
|
var events = user.ResetableData.SimRoomData.Events;
|
||||||
|
var simRoomEventIndex = events.FindIndex(x => x.Location.Chapter == location.Chapter && x.Location.Stage == location.Stage && x.Location.Order == location.Order);
|
||||||
|
if (simRoomEventIndex < 0)
|
||||||
|
{
|
||||||
|
Logging.Warn("Not Fond UserSimRoomEvent");
|
||||||
|
await WriteDataAsync(response);
|
||||||
|
}
|
||||||
|
SimRoomHelper.UpdateUserSimRoomEvent(user, index: simRoomEventIndex, events: events, selectionNumber: req.SelectionNumber, isDone: true);
|
||||||
|
|
||||||
|
JsonDb.Save();
|
||||||
|
await WriteDataAsync(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
113
EpinelPS/LobbyServer/Simroom/ProceedNikkeFunction.cs
Normal file
113
EpinelPS/LobbyServer/Simroom/ProceedNikkeFunction.cs
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
using EpinelPS.Data;
|
||||||
|
using EpinelPS.Database;
|
||||||
|
using EpinelPS.Utils;
|
||||||
|
|
||||||
|
namespace EpinelPS.LobbyServer.Simroom
|
||||||
|
{
|
||||||
|
[PacketPath("/simroom/proceednikkefunction")]
|
||||||
|
public class ProceedNikkeFunction : LobbyMsgHandler
|
||||||
|
{
|
||||||
|
protected override async Task HandleAsync()
|
||||||
|
{
|
||||||
|
// { "location": { "chapter": 3, "stage": 6, "order": 1 }, "event": 10040, "selectionNumber": 1, "selectionGroupElementId": 100401 }
|
||||||
|
ReqProceedSimRoomNikkeFunction req = await ReadData<ReqProceedSimRoomNikkeFunction>();
|
||||||
|
User user = GetUser();
|
||||||
|
|
||||||
|
ResProceedSimRoomNikkeFunction response = new()
|
||||||
|
{
|
||||||
|
Result = SimRoomResult.Success
|
||||||
|
};
|
||||||
|
|
||||||
|
var location = req.Location;
|
||||||
|
// Check
|
||||||
|
var events = user.ResetableData.SimRoomData.Events;
|
||||||
|
var simRoomEventIndex = events.FindIndex(x => x.Location.Chapter == location.Chapter && x.Location.Stage == location.Stage && x.Location.Order == location.Order);
|
||||||
|
if (simRoomEventIndex < 0)
|
||||||
|
{
|
||||||
|
Logging.Warn("Not Fond UserSimRoomEvent");
|
||||||
|
await WriteDataAsync(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// changedHps
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (GameData.Instance.SimulationRoomSelectionGroupTable.TryGetValue(req.SelectionGroupElementId, out var selectionGroup))
|
||||||
|
{
|
||||||
|
if (selectionGroup.EventFunctionType == SimulationRoomEventFunctionType.Heal)
|
||||||
|
{
|
||||||
|
if (selectionGroup.EventFunctionTargetType == SimulationRoomEventfunctionTargetType.All)
|
||||||
|
{
|
||||||
|
var changedRemainingHps = UpdateUserRemainingHps(user, selectionGroup.EventFunctionValue, type: SimulationRoomEventFunctionType.Heal);
|
||||||
|
response.ChangedHps.AddRange(changedRemainingHps.Select(x => new NetSimRoomCharacterHp { Csn = x.Csn, Hp = x.Hp }));
|
||||||
|
|
||||||
|
SimRoomHelper.UpdateUserSimRoomEvent(user, index: simRoomEventIndex, events, selectionNumber: req.SelectionNumber, isDone: true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logging.Warn($"Not implement EventFunctionTargetType: {selectionGroup.EventFunctionTargetType}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (selectionGroup.EventFunctionType == SimulationRoomEventFunctionType.Resurrection)
|
||||||
|
{
|
||||||
|
if (selectionGroup.EventFunctionTargetType == SimulationRoomEventfunctionTargetType.All)
|
||||||
|
{
|
||||||
|
var changedRemainingHps = UpdateUserRemainingHps(user, selectionGroup.EventFunctionValue, type: SimulationRoomEventFunctionType.Resurrection);
|
||||||
|
response.ChangedHps.AddRange(changedRemainingHps.Select(x => new NetSimRoomCharacterHp { Csn = x.Csn, Hp = x.Hp }));
|
||||||
|
|
||||||
|
SimRoomHelper.UpdateUserSimRoomEvent(user, index: simRoomEventIndex, events, selectionNumber: req.SelectionNumber, isDone: true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logging.Warn($"Not implement EventFunctionTargetType: {selectionGroup.EventFunctionTargetType}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logging.Warn($"Not implement EventFunctionType: {selectionGroup.EventFunctionType}");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logging.Warn("Not Fond SimulationRoomSelectionGroup");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logging.Warn($"ProceedNikkeFunction ChangedHps Exception {e.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Teams
|
||||||
|
var team = SimRoomHelper.GetTeamData(user, 1, null);
|
||||||
|
if (team is not null) response.Teams.Add(team);
|
||||||
|
|
||||||
|
JsonDb.Save();
|
||||||
|
await WriteDataAsync(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<SimRoomCharacterHp> UpdateUserRemainingHps(User user, int HpValue, SimulationRoomEventFunctionType type = SimulationRoomEventFunctionType.Heal)
|
||||||
|
{
|
||||||
|
var remainingHps = user.ResetableData.SimRoomData.RemainingHps;
|
||||||
|
if (remainingHps is not null && remainingHps.Count > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < remainingHps.Count; i++)
|
||||||
|
{
|
||||||
|
if (type is SimulationRoomEventFunctionType.Heal && remainingHps[i].Hp >= 0)
|
||||||
|
{
|
||||||
|
// Heal
|
||||||
|
remainingHps[i] = new SimRoomCharacterHp { Csn = remainingHps[i].Csn, Hp = HpValue };
|
||||||
|
}
|
||||||
|
else if (type is SimulationRoomEventFunctionType.Resurrection && remainingHps[i].Hp < 0)
|
||||||
|
{
|
||||||
|
// Resurrection
|
||||||
|
remainingHps[i] = new SimRoomCharacterHp { Csn = remainingHps[i].Csn, Hp = HpValue };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
user.ResetableData.SimRoomData.RemainingHps = remainingHps;
|
||||||
|
return remainingHps;
|
||||||
|
}
|
||||||
|
return remainingHps;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,35 @@ namespace EpinelPS.LobbyServer.Simroom
|
|||||||
Result = SimRoomResult.Success,
|
Result = SimRoomResult.Success,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var item in req.BuffsToAdd)
|
||||||
|
{
|
||||||
|
if (!user.ResetableData.SimRoomData.LegacyBuffs.Contains(item))
|
||||||
|
user.ResetableData.SimRoomData.LegacyBuffs.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logging.Warn($"QuitSimRoom BuffsToAdd Exception {e.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var item in req.BuffsToDelete)
|
||||||
|
{
|
||||||
|
user.ResetableData.SimRoomData.LegacyBuffs.Remove(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logging.Warn($"QuitSimRoom BuffsToDelete Exception {e.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
user.ResetableData.SimRoomData.Entered = false;
|
user.ResetableData.SimRoomData.Entered = false;
|
||||||
|
user.ResetableData.SimRoomData.Events = [];
|
||||||
|
user.ResetableData.SimRoomData.RemainingHps = [];
|
||||||
|
user.ResetableData.SimRoomData.Buffs = [];
|
||||||
|
|
||||||
JsonDb.Save();
|
JsonDb.Save();
|
||||||
|
|
||||||
|
|||||||
76
EpinelPS/LobbyServer/Simroom/SelectBuff.cs
Normal file
76
EpinelPS/LobbyServer/Simroom/SelectBuff.cs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
using EpinelPS.Data;
|
||||||
|
using EpinelPS.Database;
|
||||||
|
using EpinelPS.Utils;
|
||||||
|
|
||||||
|
namespace EpinelPS.LobbyServer.Simroom
|
||||||
|
{
|
||||||
|
[PacketPath("/simroom/selectbuff")]
|
||||||
|
public class SelectBuff : LobbyMsgHandler
|
||||||
|
{
|
||||||
|
protected override async Task HandleAsync()
|
||||||
|
{
|
||||||
|
// {"location":{"chapter":3,"stage":4,"order":3},"event":111011152,"buffToAdd":1030504}
|
||||||
|
ReqSelectSimRoomBuff req = await ReadData<ReqSelectSimRoomBuff>();
|
||||||
|
User user = GetUser();
|
||||||
|
|
||||||
|
ResSelectSimRoomBuff response = new()
|
||||||
|
{
|
||||||
|
Result = SimRoomResult.Success
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update User SimRoomData Buffs
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var buffs = user.ResetableData.SimRoomData.Buffs;
|
||||||
|
if (req.BuffToDelete > 0)
|
||||||
|
{
|
||||||
|
if (buffs.Contains(req.BuffToDelete)) buffs.Remove(req.BuffToDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.BuffToAdd > 0)
|
||||||
|
{
|
||||||
|
if (!buffs.Contains(req.BuffToDelete)) buffs.Add(req.BuffToAdd);
|
||||||
|
}
|
||||||
|
user.ResetableData.SimRoomData.Buffs = buffs;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logging.Warn($"Update User SimRoomData Buffs Exception: {e.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetRewardData Reward
|
||||||
|
// NetRewardData RewardByRewardUpEvent
|
||||||
|
// NetRewardData RewardByOverclock
|
||||||
|
|
||||||
|
var location = req.Location;
|
||||||
|
if (location is not null)
|
||||||
|
{
|
||||||
|
var events = user.ResetableData.SimRoomData.Events;
|
||||||
|
var simRoomEventIndex = events.FindIndex(x => x.Location.Chapter == location.Chapter && x.Location.Stage == location.Stage && x.Location.Order == location.Order);
|
||||||
|
if (simRoomEventIndex > -1)
|
||||||
|
{
|
||||||
|
// Update User SimRoomData Events
|
||||||
|
SimRoomHelper.UpdateUserSimRoomEvent(user, simRoomEventIndex, events, battleProgress: (int)SimRoomBattleEventProgress.RewardReceived);
|
||||||
|
|
||||||
|
// Reward
|
||||||
|
var sorted = events.OrderBy(x => x.Location.Stage).ThenBy(x => x.Location.Order).ToList(); // Sort by Stage, Order
|
||||||
|
var last = sorted[^1]; // Get last event
|
||||||
|
if (last.Location.Chapter == location.Chapter && last.Location.Stage == location.Stage && last.Location.Order == location.Order)
|
||||||
|
{
|
||||||
|
var difficulty = user.ResetableData.SimRoomData.CurrentDifficulty;
|
||||||
|
var reward = SimRoomHelper.SimRoomReceivedReward(user, difficulty, location.Chapter);
|
||||||
|
if (reward is not null) response.Reward = reward;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonDb.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
await WriteDataAsync(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,14 +16,18 @@ namespace EpinelPS.LobbyServer.Simroom
|
|||||||
Result = SimRoomResult.Success,
|
Result = SimRoomResult.Success,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
user.ResetableData.SimRoomData.Entered = true;
|
user.ResetableData.SimRoomData.Entered = true;
|
||||||
user.ResetableData.SimRoomData.CurrentDifficulty = req.Difficulty;
|
user.ResetableData.SimRoomData.CurrentDifficulty = req.Difficulty;
|
||||||
user.ResetableData.SimRoomData.CurrentChapter = req.StartingChapter;
|
user.ResetableData.SimRoomData.CurrentChapter = req.StartingChapter;
|
||||||
|
|
||||||
// TODO: generate buffs
|
|
||||||
|
|
||||||
JsonDb.Save();
|
JsonDb.Save();
|
||||||
|
|
||||||
|
List<NetSimRoomEvent> events = SimRoomHelper.GetSimRoomEvents(user);
|
||||||
|
user.ResetableData.SimRoomData.Events = [.. events.Select(SimRoomHelper.NetToM)];
|
||||||
|
JsonDb.Save();
|
||||||
|
|
||||||
|
response.Events.AddRange(events);
|
||||||
|
|
||||||
await WriteDataAsync(response);
|
await WriteDataAsync(response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
23
EpinelPS/LobbyServer/Simroom/SelectEvent.cs
Normal file
23
EpinelPS/LobbyServer/Simroom/SelectEvent.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using EpinelPS.Utils;
|
||||||
|
using EpinelPS.Database;
|
||||||
|
|
||||||
|
namespace EpinelPS.LobbyServer.Simroom
|
||||||
|
{
|
||||||
|
[PacketPath("/simroom/selectevent")]
|
||||||
|
public class SelectEvent : LobbyMsgHandler
|
||||||
|
{
|
||||||
|
protected override async Task HandleAsync()
|
||||||
|
{
|
||||||
|
// { "location": { "chapter": 3, "stage": 1, "order": 3 }, "event": 111021115 }
|
||||||
|
ReqSelectSimRoomEvent req = await ReadData<ReqSelectSimRoomEvent>();
|
||||||
|
// User user = GetUser();
|
||||||
|
|
||||||
|
ResSelectSimRoomEvent response = new()
|
||||||
|
{
|
||||||
|
Result = SimRoomResult.Success,
|
||||||
|
};
|
||||||
|
|
||||||
|
await WriteDataAsync(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
EpinelPS/LobbyServer/Simroom/SelectSelectionEvent.cs
Normal file
34
EpinelPS/LobbyServer/Simroom/SelectSelectionEvent.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using EpinelPS.Database;
|
||||||
|
using EpinelPS.Utils;
|
||||||
|
|
||||||
|
namespace EpinelPS.LobbyServer.Simroom
|
||||||
|
{
|
||||||
|
[PacketPath("/simroom/selectselectionevent")]
|
||||||
|
public class SelectSelectionEvent : LobbyMsgHandler
|
||||||
|
{
|
||||||
|
protected override async Task HandleAsync()
|
||||||
|
{
|
||||||
|
ReqSelectSimRoomSelectionEvent req = await ReadData<ReqSelectSimRoomSelectionEvent>();
|
||||||
|
User user = GetUser();
|
||||||
|
|
||||||
|
ResSelectSimRoomSelectionEvent response = new()
|
||||||
|
{
|
||||||
|
Result = SimRoomResult.Success
|
||||||
|
};
|
||||||
|
|
||||||
|
var location = req.Location;
|
||||||
|
// Check
|
||||||
|
var events = user.ResetableData.SimRoomData.Events;
|
||||||
|
var simRoomEventIndex = events.FindIndex(x => x.Location.Chapter == location.Chapter && x.Location.Stage == location.Stage && x.Location.Order == location.Order);
|
||||||
|
if (simRoomEventIndex < 0)
|
||||||
|
{
|
||||||
|
Logging.Warn("Not Fond UserSimRoomEvent");
|
||||||
|
await WriteDataAsync(response);
|
||||||
|
}
|
||||||
|
SimRoomHelper.UpdateUserSimRoomEvent(user, index: simRoomEventIndex, events: events, selectionNumber: req.SelectionNumber);
|
||||||
|
|
||||||
|
JsonDb.Save();
|
||||||
|
await WriteDataAsync(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
633
EpinelPS/LobbyServer/Simroom/SimRoomHelper.cs
Normal file
633
EpinelPS/LobbyServer/Simroom/SimRoomHelper.cs
Normal file
@@ -0,0 +1,633 @@
|
|||||||
|
using EpinelPS.Data;
|
||||||
|
using EpinelPS.Database;
|
||||||
|
using EpinelPS.Utils;
|
||||||
|
using log4net;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace EpinelPS.LobbyServer.Simroom
|
||||||
|
{
|
||||||
|
public static class SimRoomHelper
|
||||||
|
{
|
||||||
|
private static readonly ILog log = LogManager.GetLogger(typeof(SimRoomHelper));
|
||||||
|
private static readonly Random _rand = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get SimRoomEvent By User or DifficultyId and ChapterId
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user"> User </param>
|
||||||
|
/// <param name="difficultyId"> DifficultyId </param>
|
||||||
|
/// <param name="chapterId"> ChapterId </param>
|
||||||
|
/// <returns>The list of NetSimRoomEvent</returns>
|
||||||
|
public static List<NetSimRoomEvent> GetSimRoomEvents(User user, int difficultyId = 0, int chapterId = 0)
|
||||||
|
{
|
||||||
|
List<NetSimRoomEvent> netSimRooms = [];
|
||||||
|
int currentDifficulty = user.ResetableData.SimRoomData.CurrentDifficulty;
|
||||||
|
int currentChapter = user.ResetableData.SimRoomData.CurrentChapter;
|
||||||
|
|
||||||
|
if (difficultyId > 0) currentDifficulty = difficultyId;
|
||||||
|
if (chapterId > 0) currentChapter = chapterId;
|
||||||
|
|
||||||
|
var events = user.ResetableData.SimRoomData.Events;
|
||||||
|
if (events.Count > 1)
|
||||||
|
{
|
||||||
|
return [.. events.Select(MToNet)];
|
||||||
|
}
|
||||||
|
|
||||||
|
var simRoomChapter = GameData.Instance.SimulationRoomChapterTable.Values.FirstOrDefault(x => x.Chapter == currentChapter && x.DifficultyId == currentDifficulty);
|
||||||
|
log.Debug($"Fond SimulationRoomChapter Chapter: {currentChapter}, DifficultyId: {currentDifficulty} Data: {JsonConvert.SerializeObject(simRoomChapter)}");
|
||||||
|
|
||||||
|
var buffPreviewRecords = GameData.Instance.SimulationRoomBuffPreviewTable.Values.ToList();
|
||||||
|
var simRoomBattleEventRecords = GameData.Instance.SimulationRoomBattleEventTable.Values.ToList();
|
||||||
|
var simRoomSelectionEventRecords = GameData.Instance.SimulationRoomSelectionEventTable.Values.ToList();
|
||||||
|
var simRoomSelectionEventGroupRecords = GameData.Instance.SimulationRoomSelectionGroupTable.Values.ToList();
|
||||||
|
|
||||||
|
if (simRoomChapter is null) return netSimRooms;
|
||||||
|
|
||||||
|
for (int i = 1; i <= simRoomChapter.StageValue; i++)
|
||||||
|
{
|
||||||
|
if (i == simRoomChapter.StageValue) // BossBattle
|
||||||
|
{
|
||||||
|
var battleBuffPreviews = GameData.Instance.SimulationRoomBuffPreviewTable.Values.Where(x => x.EventType is SimulationRoomEvent.BossBattle).ToList();
|
||||||
|
var randomBuffPreview = GetRandomItems(battleBuffPreviews, 1);
|
||||||
|
var simRoomBattleEvent = CreateSimRoomBattleEvent(chapter: simRoomChapter, stage: i, order: 1, simRoomBattleEventRecords, randomBuffPreview[0]);
|
||||||
|
events.Add(NetToM(simRoomBattleEvent));
|
||||||
|
netSimRooms.Add(simRoomBattleEvent);
|
||||||
|
}
|
||||||
|
else if (i == simRoomChapter.StageValue - 1) // Selection
|
||||||
|
{
|
||||||
|
// Maintenance Selection
|
||||||
|
var simRoomSelectionEventRecord = simRoomSelectionEventRecords.OrderBy(x => x.Id).ToList()
|
||||||
|
.FindLast(x => x.EventType is SimulationRoomEvent.Maintenance);
|
||||||
|
|
||||||
|
var simRoomEvent = new NetSimRoomEvent()
|
||||||
|
{
|
||||||
|
Location = new NetSimRoomEventLocationInfo { Chapter = simRoomChapter.Chapter, Stage = i, Order = 1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (simRoomSelectionEventRecord is not null)
|
||||||
|
{
|
||||||
|
simRoomEvent.Selection = new NetSimRoomSelectionEvent
|
||||||
|
{
|
||||||
|
Id = simRoomSelectionEventRecord.Id,
|
||||||
|
SelectedNumber = 1
|
||||||
|
};
|
||||||
|
var groupRecordsBySelectionGroupId = simRoomSelectionEventGroupRecords.FindAll(x => x.SelectionGroupId == simRoomSelectionEventRecord.SelectionGroupId);
|
||||||
|
foreach (var groupRecord in groupRecordsBySelectionGroupId)
|
||||||
|
{
|
||||||
|
simRoomEvent.Selection.Group.Add(new NetSimRoomSelectionGroupElement { SelectionNumber = groupRecord.SelectionNumber, Id = groupRecord.Id });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
events.Add(NetToM(simRoomEvent));
|
||||||
|
netSimRooms.Add(simRoomEvent);
|
||||||
|
log.Debug($"stage: {i}, NetSimRoomEvent: {JsonConvert.SerializeObject(simRoomEvent)}");
|
||||||
|
|
||||||
|
// Random
|
||||||
|
var RandomSimRoomSelectionEventRecords = simRoomSelectionEventRecords.FindAll(x
|
||||||
|
=> x.EventType is SimulationRoomEvent.RandomSelection or SimulationRoomEvent.EnhanceBuff);
|
||||||
|
|
||||||
|
var RandomSimRoomSelectionEventRecord = GetRandomItems(RandomSimRoomSelectionEventRecords, 1);
|
||||||
|
|
||||||
|
var RandomSimRoomEvent = new NetSimRoomEvent
|
||||||
|
{
|
||||||
|
Location = new NetSimRoomEventLocationInfo { Chapter = simRoomChapter.Chapter, Stage = i, Order = 2 },
|
||||||
|
};
|
||||||
|
if (RandomSimRoomSelectionEventRecord != null && RandomSimRoomSelectionEventRecord.Count >= 1)
|
||||||
|
{
|
||||||
|
RandomSimRoomEvent.Selection = new NetSimRoomSelectionEvent
|
||||||
|
{
|
||||||
|
Id = RandomSimRoomSelectionEventRecord[0].Id,
|
||||||
|
SelectedNumber = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
var groupRecordsBySelectionGroupId = simRoomSelectionEventGroupRecords.FindAll(x
|
||||||
|
=> x.SelectionGroupId == RandomSimRoomSelectionEventRecord[0].SelectionGroupId);
|
||||||
|
|
||||||
|
foreach (var groupRecord in groupRecordsBySelectionGroupId)
|
||||||
|
{
|
||||||
|
RandomSimRoomEvent.Selection.Group.Add(new NetSimRoomSelectionGroupElement { SelectionNumber = groupRecord.SelectionNumber, Id = groupRecord.Id });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
events.Add(NetToM(RandomSimRoomEvent));
|
||||||
|
netSimRooms.Add(RandomSimRoomEvent);
|
||||||
|
log.Debug($"stage: {i}, NetSimRoomEvent: {JsonConvert.SerializeObject(RandomSimRoomEvent)}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var battleBuffPreviews = buffPreviewRecords.FindAll(x
|
||||||
|
=> x.EventType is SimulationRoomEvent.NormalBattle or SimulationRoomEvent.EliteBattle);
|
||||||
|
var randomBuffPreview = GetRandomItems(battleBuffPreviews, 3);
|
||||||
|
int order = 0;
|
||||||
|
foreach (var simRoomBuffPreview in randomBuffPreview)
|
||||||
|
{
|
||||||
|
order += 1;
|
||||||
|
var simRoomBattleEvent = CreateSimRoomBattleEvent(chapter: simRoomChapter, stage: i, order: order, simRoomBattleEventRecords, simRoomBuffPreview);
|
||||||
|
events.Add(NetToM(simRoomBattleEvent));
|
||||||
|
netSimRooms.Add(simRoomBattleEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// user.AddTrigger()
|
||||||
|
user.ResetableData.SimRoomData.Events = events;
|
||||||
|
JsonDb.Save();
|
||||||
|
return netSimRooms;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get BuffOptions By User And SimRoomEventLocationInfo
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user"> User </param>
|
||||||
|
/// <param name="location"> NetSimRoomEventLocationInfo </param>
|
||||||
|
/// <returns>List<int></returns>
|
||||||
|
public static List<int> GetBuffOptions(User user, NetSimRoomEventLocationInfo location)
|
||||||
|
{
|
||||||
|
var events = user.ResetableData.SimRoomData.Events;
|
||||||
|
var simRoomEventIndex = events.FindIndex(x => x.Location.Chapter == location.Chapter && x.Location.Stage == location.Stage && x.Location.Order == location.Order);
|
||||||
|
if (simRoomEventIndex > -1)
|
||||||
|
{
|
||||||
|
var simRoomEvent = events[simRoomEventIndex];
|
||||||
|
if (GameData.Instance.SimulationRoomBuffPreviewTable.TryGetValue(simRoomEvent.Battle.BuffPreviewId, out var buffPreview))
|
||||||
|
{
|
||||||
|
log.Debug($"SimRoomBuffPreview: {JsonConvert.SerializeObject(buffPreview)}");
|
||||||
|
List<SimulationRoomBuffRecord> buffRecords = [];
|
||||||
|
if (buffPreview.PreviewType is PreviewType.Bubble)
|
||||||
|
{
|
||||||
|
var bubbleType = GetBubbleType(buffPreview.PreviewTarget);
|
||||||
|
buffRecords = [.. GameData.Instance.SimulationRoomBuffTable.Values
|
||||||
|
.Where(x => x.BubbleType == bubbleType
|
||||||
|
&& x.Grade is SimulationRoomBuffGrade.SR or SimulationRoomBuffGrade.SSR )];
|
||||||
|
}
|
||||||
|
else if (buffPreview.PreviewType is PreviewType.MainTarget)
|
||||||
|
{
|
||||||
|
var mainTarget = GetMainTarget(buffPreview.PreviewTarget);
|
||||||
|
buffRecords = [.. GameData.Instance.SimulationRoomBuffTable.Values
|
||||||
|
.Where(x => x.MainTarget == mainTarget
|
||||||
|
&& x.Grade is SimulationRoomBuffGrade.SR or SimulationRoomBuffGrade.SSR)];
|
||||||
|
}
|
||||||
|
else if (buffPreview.PreviewType is PreviewType.Grade)
|
||||||
|
{
|
||||||
|
buffRecords = [.. GameData.Instance.SimulationRoomBuffTable.Values
|
||||||
|
.Where(x => x.Grade == SimulationRoomBuffGrade.EPIC)];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log.Warn($"Not Implement SimulationRoomBuffPreview.PreviewType: {buffPreview.PreviewType}");
|
||||||
|
}
|
||||||
|
if (buffRecords.Count > 0)
|
||||||
|
{
|
||||||
|
var selectedBuffs = buffRecords.GetRandomItems(3);
|
||||||
|
log.Debug($"Selected Buffs: {JsonConvert.SerializeObject(selectedBuffs)}");
|
||||||
|
|
||||||
|
// user SimRoomEvent update
|
||||||
|
UpdateUserSimRoomEvent(user, simRoomEventIndex, events, battleProgress: (int)SimRoomBattleEventProgress.RewardWaiting, BuffOptions: [.. selectedBuffs.Select(x => x.Id)]);
|
||||||
|
return [.. selectedBuffs.Select(x => x.Id)];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log.Warn($"Not Font SimulationRoomBuff");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log.Warn($"Not Font User.ResetableData.SimRoomData.Events");
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get TeamData By User Or SimRoomCharacterHp
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user"> User </param>
|
||||||
|
/// <param name="teamNumber"> Int </param>
|
||||||
|
/// <param name="remainingHps"> List<NetSimRoomCharacterHp> </param>
|
||||||
|
/// <returns> NetTeamData </returns>
|
||||||
|
public static NetTeamData GetTeamData(User user, int teamNumber, List<NetSimRoomCharacterHp>? remainingHps)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (remainingHps is not null && remainingHps.Count > 0)
|
||||||
|
{
|
||||||
|
var team = new NetTeamData()
|
||||||
|
{
|
||||||
|
TeamNumber = teamNumber
|
||||||
|
};
|
||||||
|
int slot = 1;
|
||||||
|
foreach (var item in remainingHps)
|
||||||
|
{
|
||||||
|
team.Slots.Add(new NetTeamSlot() { Slot = slot, Value = item.Csn });
|
||||||
|
slot += 1;
|
||||||
|
}
|
||||||
|
return team;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (user.UserTeams.TryGetValue((int)TeamType.SimulationRoom, out var teamData))
|
||||||
|
{
|
||||||
|
var team = teamData.Teams.FirstOrDefault(t => t.TeamNumber == teamNumber);
|
||||||
|
if (team is not null) return team;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.Error($"Get User Teams Exception: {e.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// default value is null
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Randomly select a specified number of elements from the list.
|
||||||
|
/// </summary>
|
||||||
|
public static List<T> GetRandomItems<T>(this List<T>? list, int count)
|
||||||
|
{
|
||||||
|
if (list is null || list.Count == 0)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
// If the number of requests exceeds the list length, retrieve all.
|
||||||
|
count = Math.Min(count, list.Count);
|
||||||
|
|
||||||
|
return [.. list.OrderBy(x => _rand.Next()).Take(count)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create SimRoomBattleEvent
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="chapter"> SimulationRoomChapterRecord </param>
|
||||||
|
/// <param name="stage"></param>
|
||||||
|
/// <param name="order"></param>
|
||||||
|
/// <param name="simRoomBattleEventRecords"></param>
|
||||||
|
/// <param name="randomBuffPreview"></param>
|
||||||
|
/// <returns>NetSimRoomEvent</returns>
|
||||||
|
private static NetSimRoomEvent CreateSimRoomBattleEvent(SimulationRoomChapterRecord chapter, int stage, int order,
|
||||||
|
List<SimulationRoomBattleEventRecord> simRoomBattleEventRecords, SimulationRoomBuffPreviewRecord randomBuffPreview)
|
||||||
|
{
|
||||||
|
var simRoomEvent = new NetSimRoomEvent();
|
||||||
|
var location = new NetSimRoomEventLocationInfo();
|
||||||
|
var battle = new NetSimRoomBattleEvent();
|
||||||
|
|
||||||
|
location.Chapter = chapter.Chapter;
|
||||||
|
location.Stage = stage;
|
||||||
|
location.Order = order;
|
||||||
|
var simRoomBuffPreviewBattleEvents = simRoomBattleEventRecords.FindAll(x
|
||||||
|
=> x.EventType == randomBuffPreview.EventType && x.DifficultyConditionValue <= chapter.DifficultyId);
|
||||||
|
log.Debug($"EventType: {randomBuffPreview.EventType}, SimRoomBattleEventRecord Count: {simRoomBuffPreviewBattleEvents.Count}");
|
||||||
|
var randomBattleEvents = GetRandomItems(simRoomBuffPreviewBattleEvents, 1);
|
||||||
|
foreach (var battleEvent in randomBattleEvents)
|
||||||
|
{
|
||||||
|
battle.Id = battleEvent.Id;
|
||||||
|
battle.RemainingTargetHealth = 10000;
|
||||||
|
battle.BuffPreviewId = randomBuffPreview.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
simRoomEvent.Location = location;
|
||||||
|
simRoomEvent.Battle = battle;
|
||||||
|
log.Debug($"stage: {stage}, NetSimRoomEvent: {JsonConvert.SerializeObject(simRoomEvent)}");
|
||||||
|
return simRoomEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NetSimRoomEvent MToNet(SimRoomEvent simRoomEvent)
|
||||||
|
{
|
||||||
|
var netSimRoomEvent = new NetSimRoomEvent
|
||||||
|
{
|
||||||
|
Selected = simRoomEvent.Selected,
|
||||||
|
};
|
||||||
|
if (simRoomEvent.Location is not null && simRoomEvent.Location.Chapter > 0)
|
||||||
|
{
|
||||||
|
netSimRoomEvent.Location = new NetSimRoomEventLocationInfo
|
||||||
|
{
|
||||||
|
Chapter = simRoomEvent.Location.Chapter,
|
||||||
|
Stage = simRoomEvent.Location.Stage,
|
||||||
|
Order = simRoomEvent.Location.Order
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (simRoomEvent.Battle is not null && simRoomEvent.Battle.Id > 0)
|
||||||
|
{
|
||||||
|
netSimRoomEvent.Battle = new NetSimRoomBattleEvent
|
||||||
|
{
|
||||||
|
Id = simRoomEvent.Battle.Id,
|
||||||
|
BuffOptions = { simRoomEvent.Battle.BuffOptions },
|
||||||
|
Progress = (SimRoomBattleEventProgress)simRoomEvent.Battle.Progress,
|
||||||
|
RemainingTargetHealth = simRoomEvent.Battle.RemainingTargetHealth,
|
||||||
|
BuffPreviewId = simRoomEvent.Battle.BuffPreviewId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (simRoomEvent.Selection is not null && simRoomEvent.Selection.Id > 0)
|
||||||
|
{
|
||||||
|
netSimRoomEvent.Selection = new NetSimRoomSelectionEvent
|
||||||
|
{
|
||||||
|
Id = simRoomEvent.Selection.Id,
|
||||||
|
SelectedNumber = simRoomEvent.Selection.SelectedNumber,
|
||||||
|
};
|
||||||
|
simRoomEvent.Selection.Group.ForEach(g =>
|
||||||
|
{
|
||||||
|
netSimRoomEvent.Selection.Group.Add(new NetSimRoomSelectionGroupElement
|
||||||
|
{
|
||||||
|
SelectionNumber = g.SelectionNumber,
|
||||||
|
Id = g.Id,
|
||||||
|
IsDone = g.IsDone,
|
||||||
|
RandomBuff = g.RandomBuff,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return netSimRoomEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SimRoomEvent NetToM(NetSimRoomEvent simRoomEvent)
|
||||||
|
{
|
||||||
|
var netSimRoomEvent = new SimRoomEvent
|
||||||
|
{
|
||||||
|
Selected = simRoomEvent.Selected,
|
||||||
|
EventCase = (int)simRoomEvent.EventCase,
|
||||||
|
};
|
||||||
|
if (simRoomEvent.Location is not null && simRoomEvent.Location.Chapter > 0)
|
||||||
|
{
|
||||||
|
netSimRoomEvent.Location = new SimRoomEventLocationInfo
|
||||||
|
{
|
||||||
|
Chapter = simRoomEvent.Location.Chapter,
|
||||||
|
Stage = simRoomEvent.Location.Stage,
|
||||||
|
Order = simRoomEvent.Location.Order
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (simRoomEvent.Battle is not null && simRoomEvent.Battle.Id > 0)
|
||||||
|
{
|
||||||
|
netSimRoomEvent.Battle = new SimRoomBattleEvent
|
||||||
|
{
|
||||||
|
Id = simRoomEvent.Battle.Id,
|
||||||
|
Progress = (int)simRoomEvent.Battle.Progress,
|
||||||
|
RemainingTargetHealth = simRoomEvent.Battle.RemainingTargetHealth,
|
||||||
|
BuffPreviewId = simRoomEvent.Battle.BuffPreviewId,
|
||||||
|
};
|
||||||
|
foreach (var item in simRoomEvent.Battle.BuffOptions)
|
||||||
|
{
|
||||||
|
netSimRoomEvent.Battle.BuffOptions.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (simRoomEvent.Selection is not null && simRoomEvent.Selection.Id > 0)
|
||||||
|
{
|
||||||
|
netSimRoomEvent.Selection = new SimRoomSelectionEvent
|
||||||
|
{
|
||||||
|
Id = simRoomEvent.Selection.Id,
|
||||||
|
SelectedNumber = simRoomEvent.Selection.SelectedNumber,
|
||||||
|
};
|
||||||
|
foreach (var g in simRoomEvent.Selection.Group)
|
||||||
|
{
|
||||||
|
netSimRoomEvent.Selection.Group.Add(new SimRoomSelectionGroupElement
|
||||||
|
{
|
||||||
|
SelectionNumber = g.SelectionNumber,
|
||||||
|
Id = g.Id,
|
||||||
|
IsDone = g.IsDone,
|
||||||
|
RandomBuff = g.RandomBuff,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return netSimRoomEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SimulationRoomBubbleType GetBubbleType(string previewTarget)
|
||||||
|
{
|
||||||
|
// Type_C
|
||||||
|
switch (previewTarget)
|
||||||
|
{
|
||||||
|
case "Type_A":
|
||||||
|
return SimulationRoomBubbleType.TypeA;
|
||||||
|
case "Type_B":
|
||||||
|
return SimulationRoomBubbleType.TypeB;
|
||||||
|
case "Type_C":
|
||||||
|
return SimulationRoomBubbleType.TypeC;
|
||||||
|
case "Type_D":
|
||||||
|
return SimulationRoomBubbleType.TypeD;
|
||||||
|
default:
|
||||||
|
log.Warn("Unknown preview target: " + previewTarget);
|
||||||
|
return SimulationRoomBubbleType.TypeA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SimulationRoomBuffMainTarget GetMainTarget(string previewTarget)
|
||||||
|
{
|
||||||
|
// Shoot = 0, Attack = 1, Survive = 2
|
||||||
|
switch (previewTarget)
|
||||||
|
{
|
||||||
|
case "Shoot":
|
||||||
|
return SimulationRoomBuffMainTarget.Shoot;
|
||||||
|
case "Attack":
|
||||||
|
return SimulationRoomBuffMainTarget.Attack;
|
||||||
|
case "Survive":
|
||||||
|
return SimulationRoomBuffMainTarget.Survive;
|
||||||
|
default:
|
||||||
|
log.Warn("Unknown preview target: " + previewTarget);
|
||||||
|
return SimulationRoomBuffMainTarget.Attack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update User SimRoomEvent Events
|
||||||
|
/// </summary>
|
||||||
|
public static void UpdateUserSimRoomEvent(User user, int index, List<SimRoomEvent> events, int selectionNumber = 0,
|
||||||
|
bool isDone = false, int battleProgress = 0, List<int>? BuffOptions = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var simRoomEvent = events[index];
|
||||||
|
// user SimRoomEvent update
|
||||||
|
simRoomEvent.Selected = true;
|
||||||
|
|
||||||
|
// Update Selection Group
|
||||||
|
var groupIndex = simRoomEvent.Selection.Group.FindIndex(x => x.SelectionNumber == selectionNumber);
|
||||||
|
if (groupIndex > -1 && isDone)
|
||||||
|
{
|
||||||
|
var group = simRoomEvent.Selection.Group[groupIndex];
|
||||||
|
group.IsDone = isDone;
|
||||||
|
simRoomEvent.Selection.Group[groupIndex] = group;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log.Warn("Not Fond SimRoomSelectionEvent.Group");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Udapte Battle Progress
|
||||||
|
if (battleProgress > 0) simRoomEvent.Battle.Progress = battleProgress;
|
||||||
|
|
||||||
|
// Update BuffOptions
|
||||||
|
if (BuffOptions is not null && BuffOptions.Count > 0)
|
||||||
|
{
|
||||||
|
simRoomEvent.Battle.BuffOptions = BuffOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
events[index] = simRoomEvent;
|
||||||
|
user.ResetableData.SimRoomData.Events = events;
|
||||||
|
log.Debug($"UpdateUserSimRoomEvent After UserSimRoomEventData: {JsonConvert.SerializeObject(user.ResetableData.SimRoomData.Events)}");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.Error($"Update UserSimRoomEvent Events Exception: {e.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<SimRoomCharacterHp> UpdateUserRemainingHps(User user, List<NetSimRoomCharacterHp>? remainingHps = null, int teamNumber = 1, int HpValue = 10000)
|
||||||
|
{
|
||||||
|
var userRemainingHps = user.ResetableData.SimRoomData.RemainingHps;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// req remainingHps
|
||||||
|
if (remainingHps is not null && remainingHps.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var characterHp in remainingHps)
|
||||||
|
{
|
||||||
|
var userCharacterHpIndex = userRemainingHps.FindIndex(x => x.Csn == characterHp.Csn);
|
||||||
|
if (userCharacterHpIndex > -1)
|
||||||
|
{
|
||||||
|
userRemainingHps[userCharacterHpIndex] = new SimRoomCharacterHp { Csn = characterHp.Csn, Hp = characterHp.Hp };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
userRemainingHps.Add(new SimRoomCharacterHp { Csn = characterHp.Csn, Hp = characterHp.Hp });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get user team
|
||||||
|
if (user.UserTeams.TryGetValue((int)TeamType.SimulationRoom, out var userTeam))
|
||||||
|
{
|
||||||
|
var team = userTeam.Teams.FirstOrDefault(x => x.TeamNumber == teamNumber);
|
||||||
|
if (team is not null)
|
||||||
|
{
|
||||||
|
foreach (var slot in team.Slots)
|
||||||
|
{
|
||||||
|
if (userRemainingHps.FindIndex(x => x.Csn == slot.Value) < 0) userRemainingHps.Add(new SimRoomCharacterHp { Csn = slot.Value, Hp = HpValue });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// update user
|
||||||
|
user.ResetableData.SimRoomData.RemainingHps = userRemainingHps;
|
||||||
|
return userRemainingHps;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
log.Error($"Update UserRemainingHps Exception: {e.Message}");
|
||||||
|
}
|
||||||
|
return userRemainingHps;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static NetRewardData? SimRoomReceivedReward(User user, int difficultyId, int chapterId)
|
||||||
|
{
|
||||||
|
// check if reward is received
|
||||||
|
NetRewardData? reward = null;
|
||||||
|
if (!IsRewardReceived(user, difficultyId, chapterId))
|
||||||
|
{
|
||||||
|
|
||||||
|
var allChapterRecords = GameData.Instance.SimulationRoomChapterTable.Values;
|
||||||
|
var chapter = allChapterRecords
|
||||||
|
.FirstOrDefault(x => x.DifficultyId == difficultyId && x.Chapter == chapterId);
|
||||||
|
|
||||||
|
if (chapter is not null && chapter.RewardId > 0)
|
||||||
|
{
|
||||||
|
var receivedRewardChapters = user.ResetableData.SimRoomData.ReceivedRewardChapters;
|
||||||
|
var receivedRewardChapter = receivedRewardChapters
|
||||||
|
.OrderBy(x => x.Difficulty)
|
||||||
|
.ThenBy(x => x.Chapter)
|
||||||
|
.LastOrDefault();
|
||||||
|
SimulationRoomChapterRecord? receivedRewardChapterRecord = null;
|
||||||
|
if (receivedRewardChapter is not null)
|
||||||
|
{
|
||||||
|
receivedRewardChapterRecord = allChapterRecords
|
||||||
|
.FirstOrDefault(x => x.DifficultyId == receivedRewardChapter.Difficulty
|
||||||
|
&& x.Chapter == receivedRewardChapter.Chapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
reward = new NetRewardData();
|
||||||
|
if (receivedRewardChapterRecord is null)
|
||||||
|
{
|
||||||
|
// Claiming the reward for the first time
|
||||||
|
reward = RewardUtils.RegisterRewardsForUser(user, rewardId: chapter.RewardId);
|
||||||
|
AddRewardChapter(user, difficultyId, chapterId);
|
||||||
|
}
|
||||||
|
// Check if the received reward chapter is lower than the current chapter
|
||||||
|
else if (receivedRewardChapterRecord.DifficultyId == difficultyId && receivedRewardChapterRecord.Chapter < chapter.Chapter)
|
||||||
|
{
|
||||||
|
// Claiming the reward for a higher chapter
|
||||||
|
reward = CalculateIncrementalReward(user, chapter, receivedRewardChapterRecord);
|
||||||
|
AddRewardChapter(user, difficultyId, chapterId);
|
||||||
|
}
|
||||||
|
// Check if the received reward chapter is lower than the current difficulty
|
||||||
|
else if (receivedRewardChapterRecord.DifficultyId < difficultyId)
|
||||||
|
{
|
||||||
|
// Claiming the reward for a higher difficulty
|
||||||
|
reward = RewardUtils.RegisterRewardsForUser(user, rewardId: chapter.RewardId);
|
||||||
|
AddRewardChapter(user, difficultyId, chapterId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reward;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if reward is received
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if reward is received, otherwise false</returns>
|
||||||
|
private static bool IsRewardReceived(User user, int difficultyId, int chapterId)
|
||||||
|
{
|
||||||
|
var isReceived = user.ResetableData.SimRoomData.ReceivedRewardChapters.Any(x => x.Chapter == chapterId && x.Difficulty == difficultyId);
|
||||||
|
log.Debug($"IsRewardReceived (diff: {difficultyId}, chapter: {chapterId}): {isReceived}");
|
||||||
|
return isReceived;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Add reward chapter
|
||||||
|
/// </summary>
|
||||||
|
private static void AddRewardChapter(User user, int difficulty, int chapter)
|
||||||
|
{
|
||||||
|
user.ResetableData.SimRoomData.ReceivedRewardChapters.Add(new SimRoomChapterInfo()
|
||||||
|
{
|
||||||
|
Difficulty = difficulty,
|
||||||
|
Chapter = chapter
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculate incremental reward
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Incremental reward data</returns>
|
||||||
|
private static NetRewardData CalculateIncrementalReward(User user,
|
||||||
|
SimulationRoomChapterRecord chapter, SimulationRoomChapterRecord receivedChapterRecord)
|
||||||
|
{
|
||||||
|
var reward = new NetRewardData();
|
||||||
|
var rewardRecord = GameData.Instance.GetRewardTableEntry(chapter.RewardId);
|
||||||
|
var receivedRewardRecord = GameData.Instance.GetRewardTableEntry(receivedChapterRecord.RewardId);
|
||||||
|
|
||||||
|
if (rewardRecord?.Rewards == null || receivedRewardRecord?.Rewards == null)
|
||||||
|
{
|
||||||
|
// If rewardRecord or receivedRewardRecord is empty, return the complete reward record
|
||||||
|
reward = RewardUtils.RegisterRewardsForUser(user, rewardId: chapter.RewardId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var receivedRewardIds = receivedRewardRecord.Rewards
|
||||||
|
.Where(x => x != null)
|
||||||
|
.ToDictionary(x => x.RewardId, x => x.RewardValue);
|
||||||
|
|
||||||
|
foreach (var rewardItem in rewardRecord.Rewards.Where(x => x != null))
|
||||||
|
{
|
||||||
|
int receivedAmount = receivedRewardIds.GetValueOrDefault(rewardItem.RewardId, 0);
|
||||||
|
int remainingAmount = Math.Max(0, rewardItem.RewardValue - receivedAmount);
|
||||||
|
|
||||||
|
if (remainingAmount > 0)
|
||||||
|
{
|
||||||
|
RewardUtils.AddSingleObject(user, ref reward, rewardItem.RewardId,
|
||||||
|
rewardType: rewardItem.RewardType, remainingAmount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return reward;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
47
EpinelPS/LobbyServer/Simroom/SimpleModeSelectBuff.cs
Normal file
47
EpinelPS/LobbyServer/Simroom/SimpleModeSelectBuff.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using EpinelPS.Utils;
|
||||||
|
using EpinelPS.Database;
|
||||||
|
|
||||||
|
namespace EpinelPS.LobbyServer.Simroom
|
||||||
|
{
|
||||||
|
[PacketPath("/simroom/simplemode/selectbuff")]
|
||||||
|
public class SimpleModeSelectBuff : LobbyMsgHandler
|
||||||
|
{
|
||||||
|
protected override async Task HandleAsync()
|
||||||
|
{
|
||||||
|
// { "buffToAdd": 1010106, "buffToDelete": 1010105 }
|
||||||
|
ReqSelectSimRoomSimpleModeBuff req = await ReadData<ReqSelectSimRoomSimpleModeBuff>();
|
||||||
|
User user = GetUser();
|
||||||
|
|
||||||
|
|
||||||
|
List<int> buffs = user.ResetableData.SimRoomData.Buffs;
|
||||||
|
List<int> legacyBuffs = user.ResetableData.SimRoomData.LegacyBuffs;
|
||||||
|
if (req.BuffToDelete > 0)
|
||||||
|
{
|
||||||
|
if (buffs.Contains(req.BuffToDelete)) buffs.Remove(req.BuffToDelete);
|
||||||
|
if (legacyBuffs.Contains(req.BuffToDelete)) legacyBuffs.Remove(req.BuffToDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.BuffToAdd > 0)
|
||||||
|
{
|
||||||
|
if (!buffs.Contains(req.BuffToDelete)) buffs.Add(req.BuffToDelete);
|
||||||
|
if (!legacyBuffs.Contains(req.BuffToDelete)) legacyBuffs.Add(req.BuffToDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResSelectSimRoomSimpleModeBuff response = new()
|
||||||
|
{
|
||||||
|
Result = SimRoomResult.Reset,
|
||||||
|
NextSimpleModeBuffSelectionInfo = new()
|
||||||
|
{
|
||||||
|
RemainingBuffSelectCount = 8 - buffs.Count,
|
||||||
|
BuffOptions = { buffs },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
user.ResetableData.SimRoomData.Entered = false;
|
||||||
|
|
||||||
|
JsonDb.Save();
|
||||||
|
|
||||||
|
await WriteDataAsync(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
EpinelPS/LobbyServer/Simroom/SimpleModeSetSkipOption.cs
Normal file
24
EpinelPS/LobbyServer/Simroom/SimpleModeSetSkipOption.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using EpinelPS.Database;
|
||||||
|
using EpinelPS.Utils;
|
||||||
|
|
||||||
|
namespace EpinelPS.LobbyServer.Simroom
|
||||||
|
{
|
||||||
|
[PacketPath("/simroom/simplemode/setskipoption")]
|
||||||
|
public class SimpleModeSetSkipOption : LobbyMsgHandler
|
||||||
|
{
|
||||||
|
protected override async Task HandleAsync()
|
||||||
|
{
|
||||||
|
var req = await ReadData<ReqSetSimRoomSimpleModeSkipOption>();
|
||||||
|
// ReqSetSimRoomSimpleModeSkipOption Fields
|
||||||
|
// bool Enabled
|
||||||
|
User user = GetUser();
|
||||||
|
|
||||||
|
ResSetSimRoomSimpleModeSkipOption response = new();
|
||||||
|
|
||||||
|
user.ResetableData.SimRoomData.IsSimpleModeSkipEnabled = req.Enabled;
|
||||||
|
|
||||||
|
JsonDb.Save();
|
||||||
|
await WriteDataAsync(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
EpinelPS/LobbyServer/Simroom/SimpleModeSkipAll.cs
Normal file
41
EpinelPS/LobbyServer/Simroom/SimpleModeSkipAll.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
using EpinelPS.Database;
|
||||||
|
using EpinelPS.Utils;
|
||||||
|
|
||||||
|
namespace EpinelPS.LobbyServer.Simroom
|
||||||
|
{
|
||||||
|
[PacketPath("/simroom/simplemode/skipall")]
|
||||||
|
public class SimpleModeSkipAll : LobbyMsgHandler
|
||||||
|
{
|
||||||
|
protected override async Task HandleAsync()
|
||||||
|
{
|
||||||
|
await ReadData<ReqSkipAllSimRoomSimpleMode>();
|
||||||
|
var user = GetUser();
|
||||||
|
|
||||||
|
// ResSkipAllSimRoomSimpleMode Fields
|
||||||
|
// SimRoomResult Result
|
||||||
|
// NetRewardData Reward
|
||||||
|
// NetRewardData RewardByRewardUpEvent
|
||||||
|
|
||||||
|
ResSkipAllSimRoomSimpleMode response = new()
|
||||||
|
{
|
||||||
|
Result = SimRoomResult.Success
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reward
|
||||||
|
try
|
||||||
|
{
|
||||||
|
user.ResetableData.SimRoomData.CurrentDifficulty = 5;
|
||||||
|
user.ResetableData.SimRoomData.CurrentChapter = 3;
|
||||||
|
var reward = SimRoomHelper.SimRoomReceivedReward(user, 5, 3); // 5 = difficulty, 3 = stage
|
||||||
|
if (reward is not null) response.Reward = reward;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.WriteLine($"SkipAllSimpleMode Reward Exception: {ex.Message}", LogType.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonDb.Save();
|
||||||
|
await WriteDataAsync(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
EpinelPS/LobbyServer/Simroom/SimpleModeSkipBuffSelection.cs
Normal file
25
EpinelPS/LobbyServer/Simroom/SimpleModeSkipBuffSelection.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using EpinelPS.Utils;
|
||||||
|
using EpinelPS.Database;
|
||||||
|
|
||||||
|
namespace EpinelPS.LobbyServer.Simroom
|
||||||
|
{
|
||||||
|
[PacketPath("/simroom/simplemode/skipbuffselection")]
|
||||||
|
public class SimpleModeSkipBuffSelection : LobbyMsgHandler
|
||||||
|
{
|
||||||
|
protected override async Task HandleAsync()
|
||||||
|
{
|
||||||
|
await ReadData<ReqSkipSimRoomSimpleModeBuffSelection>();
|
||||||
|
User user = GetUser();
|
||||||
|
ResSkipSimRoomSimpleModeBuffSelection response = new()
|
||||||
|
{
|
||||||
|
Result = SimRoomResult.Reset,
|
||||||
|
};
|
||||||
|
|
||||||
|
user.ResetableData.SimRoomData.Entered = false;
|
||||||
|
|
||||||
|
JsonDb.Save();
|
||||||
|
|
||||||
|
await WriteDataAsync(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
65
EpinelPS/LobbyServer/Simroom/SimpleModeStart.cs
Normal file
65
EpinelPS/LobbyServer/Simroom/SimpleModeStart.cs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
using EpinelPS.Database;
|
||||||
|
using EpinelPS.Utils;
|
||||||
|
|
||||||
|
namespace EpinelPS.LobbyServer.Simroom
|
||||||
|
{
|
||||||
|
[PacketPath("/simroom/simplemode/start")]
|
||||||
|
public class SimpleModeStart : LobbyMsgHandler
|
||||||
|
{
|
||||||
|
protected override async Task HandleAsync()
|
||||||
|
{
|
||||||
|
await ReadData<ReqStartSimRoomSimpleMode>();
|
||||||
|
User user = GetUser();
|
||||||
|
|
||||||
|
// ResStartSimRoomSimpleMode Fields
|
||||||
|
// SimRoomResult Result
|
||||||
|
// RepeatedField<NetSimRoomEvent> Events
|
||||||
|
// NextSimpleModeBuffSelectionInfo
|
||||||
|
|
||||||
|
ResStartSimRoomSimpleMode response = new()
|
||||||
|
{
|
||||||
|
Result = SimRoomResult.Success,
|
||||||
|
// NextSimpleModeBuffSelectionInfo = new()
|
||||||
|
// {
|
||||||
|
// BuffOptions = { user.ResetableData.SimRoomData.LegacyBuffs }
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Events
|
||||||
|
try
|
||||||
|
{
|
||||||
|
user.ResetableData.SimRoomData.Entered = true;
|
||||||
|
var simRoomEvents = SimRoomHelper.GetSimRoomEvents(user, 5, 3); // 5 = difficulty, 3 = stage
|
||||||
|
if (simRoomEvents is not null)
|
||||||
|
{
|
||||||
|
foreach (var simRoomEvent in simRoomEvents)
|
||||||
|
{
|
||||||
|
// Check if event is battle and is first order
|
||||||
|
if (simRoomEvent.EventCase == NetSimRoomEvent.EventOneofCase.Battle && simRoomEvent.Location.Order == 1)
|
||||||
|
{
|
||||||
|
var location = simRoomEvent.Location;
|
||||||
|
if (location is null) continue;
|
||||||
|
SimRoomHelper.GetBuffOptions(user, location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JsonDb.Save();
|
||||||
|
|
||||||
|
var userSimRoomEvents = user.ResetableData.SimRoomData.Events;
|
||||||
|
if (userSimRoomEvents is not null)
|
||||||
|
{
|
||||||
|
simRoomEvents = [.. userSimRoomEvents.Select(SimRoomHelper.MToNet)];
|
||||||
|
}
|
||||||
|
// Add events to response
|
||||||
|
response.Events.AddRange(simRoomEvents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logging.WriteLine($"SimpleModeStart Events Exception: {ex.Message}", LogType.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
await WriteDataAsync(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -65,17 +65,17 @@ namespace EpinelPS.Models
|
|||||||
// For harmony cubes that can be equipped to multiple characters
|
// For harmony cubes that can be equipped to multiple characters
|
||||||
public List<long> CsnList = [];
|
public List<long> CsnList = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EquipmentAwakeningData
|
public class EquipmentAwakeningData
|
||||||
{
|
{
|
||||||
public long Isn;
|
public long Isn;
|
||||||
public NetEquipmentAwakeningOption Option;
|
public NetEquipmentAwakeningOption Option;
|
||||||
public bool IsNewData;
|
public bool IsNewData;
|
||||||
|
|
||||||
public EquipmentAwakeningData()
|
public EquipmentAwakeningData()
|
||||||
{
|
{
|
||||||
Option = new NetEquipmentAwakeningOption();
|
Option = new NetEquipmentAwakeningOption();
|
||||||
IsNewData = false;
|
IsNewData = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public class EventData
|
public class EventData
|
||||||
@@ -115,12 +115,66 @@ namespace EpinelPS.Models
|
|||||||
public int Defense;
|
public int Defense;
|
||||||
public int Hp;
|
public int Hp;
|
||||||
}
|
}
|
||||||
public class SimroomData
|
|
||||||
|
// Simroom Data
|
||||||
|
public class SimRoomData
|
||||||
{
|
{
|
||||||
public int CurrentDifficulty;
|
public int CurrentDifficulty;
|
||||||
public int CurrentChapter;
|
public int CurrentChapter;
|
||||||
|
public List<int> Buffs = [];
|
||||||
|
public List<int> LegacyBuffs = [];
|
||||||
|
public List<SimRoomEvent> Events = [];
|
||||||
|
public List<SimRoomCharacterHp> RemainingHps = [];
|
||||||
|
public List<SimRoomChapterInfo> ReceivedRewardChapters = [];
|
||||||
|
public bool IsSimpleModeSkipEnabled = false;
|
||||||
public bool Entered = false;
|
public bool Entered = false;
|
||||||
}
|
}
|
||||||
|
public class SimRoomEvent
|
||||||
|
{
|
||||||
|
public SimRoomEventLocationInfo Location = new();
|
||||||
|
public bool Selected;
|
||||||
|
public SimRoomBattleEvent Battle = new();
|
||||||
|
public SimRoomSelectionEvent Selection = new();
|
||||||
|
public int EventCase;
|
||||||
|
}
|
||||||
|
public class SimRoomEventLocationInfo
|
||||||
|
{
|
||||||
|
public int Chapter;
|
||||||
|
public int Stage;
|
||||||
|
public int Order;
|
||||||
|
}
|
||||||
|
public class SimRoomChapterInfo
|
||||||
|
{
|
||||||
|
public int Difficulty;
|
||||||
|
public int Chapter;
|
||||||
|
}
|
||||||
|
public class SimRoomBattleEvent
|
||||||
|
{
|
||||||
|
public int Id;
|
||||||
|
public List<int> BuffOptions = [];
|
||||||
|
public int Progress;
|
||||||
|
public int RemainingTargetHealth;
|
||||||
|
public int BuffPreviewId;
|
||||||
|
}
|
||||||
|
public class SimRoomSelectionEvent
|
||||||
|
{
|
||||||
|
public int Id;
|
||||||
|
public int SelectedNumber;
|
||||||
|
public List<SimRoomSelectionGroupElement> Group = [];
|
||||||
|
}
|
||||||
|
public class SimRoomSelectionGroupElement
|
||||||
|
{
|
||||||
|
public int SelectionNumber;
|
||||||
|
public int Id;
|
||||||
|
public bool IsDone;
|
||||||
|
public int RandomBuff;
|
||||||
|
}
|
||||||
|
public class SimRoomCharacterHp
|
||||||
|
{
|
||||||
|
public long Csn;
|
||||||
|
public int Hp;
|
||||||
|
}
|
||||||
|
|
||||||
public class ResetableData
|
public class ResetableData
|
||||||
{
|
{
|
||||||
public int WipeoutCount = 0;
|
public int WipeoutCount = 0;
|
||||||
@@ -128,10 +182,10 @@ namespace EpinelPS.Models
|
|||||||
public int InterceptionTickets = 3;
|
public int InterceptionTickets = 3;
|
||||||
public List<int> CompletedDailyMissions = [];
|
public List<int> CompletedDailyMissions = [];
|
||||||
public int DailyMissionPoints;
|
public int DailyMissionPoints;
|
||||||
public SimroomData SimRoomData = new();
|
public SimRoomData SimRoomData = new();
|
||||||
|
|
||||||
public Dictionary<int, int> DailyCounselCount = [];
|
public Dictionary<int, int> DailyCounselCount = [];
|
||||||
|
|
||||||
}
|
}
|
||||||
public class WeeklyResetableData
|
public class WeeklyResetableData
|
||||||
{
|
{
|
||||||
@@ -267,7 +321,7 @@ namespace EpinelPS.Models
|
|||||||
public bool RecievedFinalReward { get; set; }
|
public bool RecievedFinalReward { get; set; }
|
||||||
public bool CompletedPerfectly { get; set; }
|
public bool CompletedPerfectly { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PassRankData
|
public class PassRankData
|
||||||
{
|
{
|
||||||
public int PassRank { get; set; } = 0;
|
public int PassRank { get; set; } = 0;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using EpinelPS.Database;
|
|||||||
using EpinelPS.Utils;
|
using EpinelPS.Utils;
|
||||||
|
|
||||||
namespace EpinelPS.Models;
|
namespace EpinelPS.Models;
|
||||||
|
|
||||||
public class ClearedTutorialData
|
public class ClearedTutorialData
|
||||||
{
|
{
|
||||||
public int Id;
|
public int Id;
|
||||||
@@ -34,6 +35,7 @@ public class User
|
|||||||
public DateTime BanEnd;
|
public DateTime BanEnd;
|
||||||
public int BanId = 0;
|
public int BanId = 0;
|
||||||
public DateTime LastReset = DateTime.MinValue;
|
public DateTime LastReset = DateTime.MinValue;
|
||||||
|
public DateTime LastWeeklyReset = DateTime.MinValue;
|
||||||
|
|
||||||
// Game data
|
// Game data
|
||||||
public List<string> CompletedScenarios = [];
|
public List<string> CompletedScenarios = [];
|
||||||
@@ -86,7 +88,7 @@ public class User
|
|||||||
public List<int> Memorial = [];
|
public List<int> Memorial = [];
|
||||||
public List<int> JukeboxBgm = [];
|
public List<int> JukeboxBgm = [];
|
||||||
public List<NetUserFavoriteItemData> FavoriteItems = [];
|
public List<NetUserFavoriteItemData> FavoriteItems = [];
|
||||||
|
|
||||||
public List<NetUserFavoriteItemQuestData> FavoriteItemQuests = [];
|
public List<NetUserFavoriteItemQuestData> FavoriteItemQuests = [];
|
||||||
public Dictionary<int, int> TowerProgress = [];
|
public Dictionary<int, int> TowerProgress = [];
|
||||||
|
|
||||||
@@ -403,7 +405,36 @@ public class User
|
|||||||
return LastReset < todayResetTime;
|
return LastReset < todayResetTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetDataIfNeeded()
|
private bool ShouldResetWeekly()
|
||||||
|
{
|
||||||
|
var nowLocal = DateTime.UtcNow;
|
||||||
|
|
||||||
|
// Calculate the most recent Tuesday reset time
|
||||||
|
DayOfWeek currentDay = nowLocal.DayOfWeek;
|
||||||
|
int daysSinceTuesday = ((int)currentDay - (int)DayOfWeek.Tuesday + 7) % 7;
|
||||||
|
|
||||||
|
// Get the date of the most recent Tuesday
|
||||||
|
DateTime thisTuesday = nowLocal.Date.AddDays(-daysSinceTuesday);
|
||||||
|
|
||||||
|
// Compute the weekly reset time
|
||||||
|
DateTime weeklyResetTime = new(
|
||||||
|
thisTuesday.Year,
|
||||||
|
thisTuesday.Month,
|
||||||
|
thisTuesday.Day,
|
||||||
|
JsonDb.Instance.ResetHourUtcTime, 0, 0
|
||||||
|
);
|
||||||
|
|
||||||
|
// If nowLocal is before the weekly reset time, subtract a week
|
||||||
|
if (nowLocal < weeklyResetTime)
|
||||||
|
{
|
||||||
|
weeklyResetTime = weeklyResetTime.AddDays(-7);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If user's last reset was before the last scheduled 2 PM, they need reset
|
||||||
|
return LastWeeklyReset < weeklyResetTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*public void ResetDataIfNeeded()
|
||||||
{
|
{
|
||||||
if (!ShouldResetUser()) return;
|
if (!ShouldResetUser()) return;
|
||||||
|
|
||||||
@@ -412,5 +443,41 @@ public class User
|
|||||||
LastReset = DateTime.UtcNow;
|
LastReset = DateTime.UtcNow;
|
||||||
ResetableData = new();
|
ResetableData = new();
|
||||||
JsonDb.Save();
|
JsonDb.Save();
|
||||||
|
}*/
|
||||||
|
|
||||||
|
public void ResetDataIfNeeded()
|
||||||
|
{
|
||||||
|
bool needsSave = false;
|
||||||
|
|
||||||
|
// Check daily reset
|
||||||
|
if (ShouldResetUser())
|
||||||
|
{
|
||||||
|
Logging.WriteLine("Resetting daily user data...", LogType.Warning);
|
||||||
|
|
||||||
|
LastReset = DateTime.UtcNow;
|
||||||
|
ResetableData = new()
|
||||||
|
{
|
||||||
|
SimRoomData = new()
|
||||||
|
{
|
||||||
|
LegacyBuffs = ResetableData.SimRoomData.LegacyBuffs // Retain old LegacyBuffs data
|
||||||
|
}
|
||||||
|
};
|
||||||
|
needsSave = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check weekly reset
|
||||||
|
if (ShouldResetWeekly())
|
||||||
|
{
|
||||||
|
Logging.WriteLine("Resetting weekly user data...", LogType.Warning);
|
||||||
|
|
||||||
|
LastWeeklyReset = DateTime.UtcNow;
|
||||||
|
ResetableData = new();
|
||||||
|
needsSave = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsSave)
|
||||||
|
{
|
||||||
|
JsonDb.Save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
90
EpinelPS/Utils/DateTimeHelper.cs
Normal file
90
EpinelPS/Utils/DateTimeHelper.cs
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
|
||||||
|
namespace EpinelPS.Utils
|
||||||
|
{
|
||||||
|
public static class DateTimeHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Get the specified time of the next day in the specified time zone
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="timeZoneId">Time zone ID, such as "China Standard Time", "Eastern Standard Time", "UTC"</param>
|
||||||
|
/// <param name="hour">Hours (0-23)</param>
|
||||||
|
/// <param name="minute">Minutes (0-59)</param>
|
||||||
|
/// <returns>The specified time of the next day</returns>
|
||||||
|
public static DateTime GetNextDayAtTime(string timeZoneId = "", int hour = 0, int minute = 0)
|
||||||
|
{
|
||||||
|
// Get the current time of the target time zone and the current time zone
|
||||||
|
(DateTime currentTime, TimeZoneInfo tz) = GetCurrentTimeWithZone(timeZoneId);
|
||||||
|
|
||||||
|
DateTime nextDay = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, hour, minute, 0).AddDays(1);
|
||||||
|
|
||||||
|
return TimeZoneInfo.ConvertTimeToUtc(nextDay, tz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the next specified day of the week + specified time in the specified time zone
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="timeZoneId">Time zone ID, such as "China Standard Time"、"Eastern Standard Time"、"UTC"</param>
|
||||||
|
/// <param name="targetDay">Target day of the week, for example DayOfWeek.Monday</param>
|
||||||
|
/// <param name="hour">Hours (0-23)</param>
|
||||||
|
/// <param name="minute">Minutes (0-59)</param>
|
||||||
|
/// <returns>Next specified day of the week Specified time </returns>
|
||||||
|
public static DateTime GetNextWeekdayAtTime(string timeZoneId, DayOfWeek targetDay, int hour, int minute = 0)
|
||||||
|
{
|
||||||
|
// Get the current time of the target time zone and the current time zone
|
||||||
|
(DateTime currentTime, TimeZoneInfo tz) = GetCurrentTimeWithZone(timeZoneId);
|
||||||
|
|
||||||
|
// Calculate the number of days until the target weekday
|
||||||
|
int daysUntilTarget = ((int)targetDay - (int)currentTime.DayOfWeek + 7) % 7;
|
||||||
|
if (daysUntilTarget == 0) daysUntilTarget = 7; // If today is the target day of the week, take next week
|
||||||
|
|
||||||
|
DateTime nextWeekday = new DateTime(currentTime.Year, currentTime.Month, currentTime.Day, hour, minute, 0).AddDays(daysUntilTarget);
|
||||||
|
|
||||||
|
return TimeZoneInfo.ConvertTimeToUtc(nextWeekday, tz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a specific day of next month in the specified time zone at a specified time
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="timeZoneId">Time zone ID, such as "China Standard Time"、"Eastern Standard Time"、"UTC"</param>
|
||||||
|
/// <param name="day">Target date (1-31, ensure that this date exists in the month)</param>
|
||||||
|
/// <param name="hour">Hours (0-23)</param>
|
||||||
|
/// <param name="minute">Minutes (0-59)</param>
|
||||||
|
/// <returns>Specified date and time next month</returns>
|
||||||
|
public static DateTime GetNextMonthDayAtTime(string timeZoneId, int day, int hour, int minute = 0)
|
||||||
|
{
|
||||||
|
// Get the current time of the target time zone and the current time zone
|
||||||
|
(DateTime currentTime, TimeZoneInfo tz) = GetCurrentTimeWithZone(timeZoneId);
|
||||||
|
|
||||||
|
// Calculate the year and month of next month
|
||||||
|
int year = currentTime.Month == 12 ? currentTime.Year + 1 : currentTime.Year;
|
||||||
|
int month = currentTime.Month == 12 ? 1 : currentTime.Month + 1;
|
||||||
|
|
||||||
|
// Ensure the date is valid (avoid situations like 30th February)
|
||||||
|
int daysInMonth = DateTime.DaysInMonth(year, month);
|
||||||
|
int targetDay = Math.Min(day, daysInMonth);
|
||||||
|
|
||||||
|
// Construct target time
|
||||||
|
DateTime target = new(year, month, targetDay, hour, minute, 0);
|
||||||
|
|
||||||
|
return TimeZoneInfo.ConvertTimeToUtc(target, tz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the current time and timezone object for a specified timezone
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="timeZoneId">Time zone ID, such as "China Standard Time"、"Eastern Standard Time"、"UTC"</param>
|
||||||
|
/// <returns>(currentTime, tz) tuple</returns>
|
||||||
|
public static (DateTime currentTime, TimeZoneInfo tz) GetCurrentTimeWithZone(string timeZoneId)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Get the target time zone
|
||||||
|
TimeZoneInfo tz = TimeZoneInfo.Local;
|
||||||
|
if (timeZoneId != null && timeZoneId != "")
|
||||||
|
tz = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
|
||||||
|
DateTime currentTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tz);
|
||||||
|
return (currentTime, tz);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"StaticDataMpk": {
|
"StaticDataMpk": {
|
||||||
"Url": "https://cloud.nikke-kr.com/prdenv/139-cf218bd245/staticdata/data/qa-251030-10b/473550/mpk/StaticData.pack",
|
"Url": "https://cloud.nikke-kr.com/prdenv/139-c596af726e/staticdata/data/qa-251120-10b-p1/477874/mpk/StaticData.pack",
|
||||||
"Version": "data/qa-251030-10b/473550",
|
"Version": "data/qa-251120-10b-p1/477874",
|
||||||
"Salt1": "UfBN5TYtYG7pAY6lxoZXyA7tBOf1rdoPKfxbdB/6n0M=",
|
"Salt1": "t8iCAEGhWUzYFk6umjwwxY6Y4IrqGQcil/rAG6M/ofw=",
|
||||||
"Salt2": "zgmjiq+i9OdM6TjDHKav1JepFUaWLXRtismYpUk7lt4="
|
"Salt2": "eBWJaf2wR/WdkfhYEc9x7gHmZX8inyrDf6sA5l7N4zk="
|
||||||
},
|
},
|
||||||
"ResourceBaseURL": "https://cloud.nikke-kr.com/prdenv/139-bfaa7caf86/{Platform}",
|
"ResourceBaseURL": "https://cloud.nikke-kr.com/prdenv/139-b131234cad/{Platform}",
|
||||||
"GameMinVer": "100.0.1",
|
"GameMinVer": "100.0.1",
|
||||||
"GameMaxVer": "150.0.2",
|
"GameMaxVer": "150.0.2",
|
||||||
"TargetVersion": "139.8.13"
|
"TargetVersion": "139.8.13"
|
||||||
|
|||||||
Reference in New Issue
Block a user