feat: Implement SimRoom Overclock-实现模拟室超频 (#68)

- Added GetAcquireBuffFunction to handle buff acquisition requests in the simulation room.
- Added InfinitePopupCheck to manage infinite popup checks for users.
- Added ProceedSkipFunction to process skip requests in the simulation room.
- Updated SelectDifficulty to handle overclock options and season data.
- Enhanced SimRoomHelper to support overclock mechanics and event group logic.
- Modified GetSimRoomData to include buffs and legacy buffs in the response.
- Updated Quit functionality to reset overclock state upon quitting the simulation room.
- Added logic to handle overclock rewards and high score updates.
- Refactored user model to retain current season data and legacy buffs during resets.
- Introduced new OverclockData and OverclockHighScoreData models to manage overclock states.
This commit is contained in:
qmengz
2025-12-06 04:26:14 +08:00
committed by GitHub
parent 6d44c6eac9
commit 24f6de8704
18 changed files with 687 additions and 159 deletions

View File

@@ -26,3 +26,6 @@ dotnet_diagnostic.SYSLIB0039.severity = none
# CS0219: 变量已被赋值,但从未使用过它的值
dotnet_diagnostic.CS0219.severity = none
# CS8618: 类型不包含 null 值的属性
dotnet_diagnostic.CS8618.severity = none

View File

@@ -309,6 +309,16 @@ namespace EpinelPS.Data
[LoadRecord("SimulationRoomBuffTable.json", "Id")]
public readonly Dictionary<int, SimulationRoomBuffRecord> SimulationRoomBuffTable = [];
// SimulationRoom Overclock Data Tables
[LoadRecord("SimulationRoomOcLevelTable.json", "Id")]
public readonly Dictionary<int, SimulationRoomOverclockLevelRecord> SimulationRoomOcLevelTable = [];
[LoadRecord("SimulationRoomOcOptionGroupTable.json", "Id")]
public readonly Dictionary<int, SimulationRoomOverclockOptionGroupRecord> SimulationRoomOcOptionGroupTable = [];
[LoadRecord("SimulationRoomOcOptionTable.json", "Id")]
public readonly Dictionary<int, SimulationRoomOverclockOptionRecord> SimulationRoomOcOptionTable = [];
[LoadRecord("SimulationRoomOcSeasonTable.json", "Id")]
public readonly Dictionary<int, SimulationRoomOverclockSeasonRecord> SimulationRoomOcSeasonTable = [];
static async Task<GameData> BuildAsync()
{
await Load();
@@ -350,8 +360,9 @@ namespace EpinelPS.Data
{
using FileStream fileStream = File.Open(file, FileMode.Open, FileAccess.Read);
Rfc2898DeriveBytes a = new(PresharedValue, data.GetSalt2Bytes(), 10000, HashAlgorithmName.SHA256);
byte[] key2 = a.GetBytes(32);
// Rfc2898DeriveBytes a = new(PresharedValue, data.GetSalt2Bytes(), 10000, HashAlgorithmName.SHA256);
// byte[] key2 = a.GetBytes(32);
byte[] key2 = Rfc2898DeriveBytes.Pbkdf2(PresharedValue, data.GetSalt2Bytes(), 10000, HashAlgorithmName.SHA256, 32);
byte[] decryptionKey = key2[0..16];
byte[] iv = key2[16..32];
@@ -387,8 +398,9 @@ namespace EpinelPS.Data
throw new Exception("error 3");
dataMs.Position = 0;
Rfc2898DeriveBytes keyDec2 = new(PresharedValue, data.GetSalt1Bytes(), 10000, HashAlgorithmName.SHA256);
byte[] key3 = keyDec2.GetBytes(32);
// Rfc2898DeriveBytes keyDec2 = new(PresharedValue, data.GetSalt1Bytes(), 10000, HashAlgorithmName.SHA256);
// byte[] key3 = keyDec2.GetBytes(32);
byte[] key3 = Rfc2898DeriveBytes.Pbkdf2(PresharedValue, data.GetSalt1Bytes(), 10000, HashAlgorithmName.SHA256, 32);
byte[] val2 = key3[0..16];
byte[] iv2 = key3[16..32];

View File

@@ -13,7 +13,27 @@ namespace EpinelPS.LobbyServer.Character
ResGetCharacterData response = new();
foreach (CharacterModel item in user.Characters)
{
response.Character.Add(new NetUserCharacterData() { Default = new() { Csn = item.Csn, Skill1Lv = item.Skill1Lvl, Skill2Lv = item.Skill2Lvl, CostumeId = item.CostumeId, Lv = user.GetCharacterLevel(item.Csn, item.Level), Grade = item.Grade, Tid = item.Tid, UltiSkillLv = item.UltimateLevel }, IsSynchro = user.GetSynchro(item.Csn) });
response.Character.Add(new NetUserCharacterData()
{
Default = new()
{
Csn = item.Csn,
Skill1Lv = item.Skill1Lvl,
Skill2Lv = item.Skill2Lvl,
CostumeId = item.CostumeId,
Lv = user.GetCharacterLevel(item.Csn, item.Level),
Grade = item.Grade,
Tid = item.Tid,
UltiSkillLv = item.UltimateLevel
},
IsSynchro = user.GetSynchro(item.Csn)
});
// Check if character is main force
if (item.IsMainForce)
{
response.MainForceCsnList.Add(item.Csn);
}
}
List<CharacterModel> highestLevelCharacters = [.. user.Characters.OrderByDescending(x => x.Level).Take(5)];

View File

@@ -0,0 +1,29 @@
using EpinelPS.Database;
using EpinelPS.Utils;
namespace EpinelPS.LobbyServer.Character
{
[PacketPath("/character/mainforce/set")]
public class SetCharacterMainForce : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
ReqSetCharacterMainForce req = await ReadData<ReqSetCharacterMainForce>();
User user = GetUser();
foreach (CharacterModel item in user.Characters)
{
if (item.Csn == req.Csn)
{
item.IsMainForce = req.IsMainForce;
break;
}
}
JsonDb.Save();
ResSetCharacterMainForce response = new();
await WriteDataAsync(response);
}
}
}

View File

@@ -31,7 +31,7 @@ namespace EpinelPS.LobbyServer.ContentsOpen
JsonDb.Save();
}
foreach (KeyValuePair<int, UnlockData> item in user.ContentsOpenUnlocked)
foreach (KeyValuePair<int, UnlockData> item in user.ContentsOpenUnlocked.OrderBy(x => x.Key))
{
response.ContentsOpenUnlockInfoList.Add(new NetContentsOpenUnlockInfo()
{

View File

@@ -1,4 +1,5 @@
using EpinelPS.Utils;
using EpinelPS.Data;
using EpinelPS.Utils;
namespace EpinelPS.LobbyServer.LobbyUser
{
@@ -7,7 +8,7 @@ namespace EpinelPS.LobbyServer.LobbyUser
{
protected override async Task HandleAsync()
{
ReqGetContentsOpenData req = await ReadData<ReqGetContentsOpenData>();
await ReadData<ReqGetContentsOpenData>();
User user = GetUser();
// this request returns a list of "special" stages that mark when something is unlocked, ex: the shop or interception
@@ -23,12 +24,40 @@ namespace EpinelPS.LobbyServer.LobbyUser
response.ClearStageList.Add(stage);
}
}
response.MaxGachaCount = 10;
response.MaxGachaPremiumCount = 10;
response.MaxGachaCount = user.GachaTutorialPlayCount;
response.MaxGachaPremiumCount = user.GachaTutorialPlayCount;
// todo tutorial playcount of gacha
response.TutorialGachaPlayCount = user.GachaTutorialPlayCount;
// ClearSimRoomChapterList: 已通关的章节列表,用于显示超频选项 SimRoomOC
response.ClearSimRoomChapterList.AddRange(GetClearSimRoomChapterList(user));
await WriteDataAsync(response);
}
private static List<int> GetClearSimRoomChapterList(User user)
{
var clearSimRoomChapterList = new List<int>();
try
{
var currentDifficulty = user.ResetableData.SimRoomData?.CurrentDifficulty ?? 0;
var currentChapter = user.ResetableData.SimRoomData?.CurrentChapter ?? 0;
if (currentDifficulty > 0 && currentChapter > 0)
{
var chapters = GameData.Instance.SimulationRoomChapterTable.Values.Where(c => c.DifficultyId <= currentDifficulty).ToList();
foreach (var chapter in chapters)
{
bool isAdd = chapter.DifficultyId < currentDifficulty ||
(chapter.DifficultyId == currentDifficulty && chapter.Chapter <= currentChapter);
if (isAdd) clearSimRoomChapterList.Add(chapter.Id);
}
}
}
catch (Exception e)
{
Logging.Warn($"GetClearSimRoomChapterList error: {e.Message}");
}
return clearSimRoomChapterList;
}
}
}

View File

@@ -7,11 +7,16 @@ namespace EpinelPS.LobbyServer.LobbyUser
{
protected override async Task HandleAsync()
{
ReqGetUserTitleCounterList req = await ReadData<ReqGetUserTitleCounterList>();
await ReadData<ReqGetUserTitleCounterList>();
ResGetUserTitleCounterList r = new();
ResGetUserTitleCounterList response = new();
response.UserTitleCounterList.Add(new ResGetUserTitleCounterList.Types.NetUserTitleCounter { Condition = 23, SubCondition = 1, Count = 10});
response.UserTitleCounterList.Add(new ResGetUserTitleCounterList.Types.NetUserTitleCounter { Condition = 23, SubCondition = 2, Count = 10});
response.UserTitleCounterList.Add(new ResGetUserTitleCounterList.Types.NetUserTitleCounter { Condition = 23, SubCondition = 3, Count = 10});
response.UserTitleCounterList.Add(new ResGetUserTitleCounterList.Types.NetUserTitleCounter { Condition = 23, SubCondition = 4, Count = 10});
response.UserTitleCounterList.Add(new ResGetUserTitleCounterList.Types.NetUserTitleCounter { Condition = 23, SubCondition = 5, Count = 10});
await WriteDataAsync(r);
await WriteDataAsync(response);
}
}
}

View File

@@ -0,0 +1,44 @@
using EpinelPS.Database;
using EpinelPS.Utils;
namespace EpinelPS.LobbyServer.Simroom
{
[PacketPath("/simroom/getacquirebufffunction")]
public class GetAcquireBuffFunction : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
// ReqGetSimRoomAcquireBuffFunction Fields
// NetSimRoomEventLocationInfo Location
// int Event
// int SelectionNumber
// int SelectionGroupElementId
// { "location": { "chapter": 3, "stage": 6, "order": 2 }, "event": 22103, "selectionNumber": 2, "selectionGroupElementId": 221033 }
var req = await ReadData<ReqGetSimRoomAcquireBuffFunction>();
var user = GetUser();
// ResGetSimRoomAcquireBuffFunction Fields
// SimRoomResult Result
// int RandomBuff
ResGetSimRoomAcquireBuffFunction response = new()
{
Result = SimRoomResult.Success,
RandomBuff = 2020406
};
// 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);
}
}
}

View File

@@ -31,6 +31,8 @@ namespace EpinelPS.LobbyServer.Simroom
var CurrentDifficulty = user.ResetableData.SimRoomData.CurrentDifficulty;
var currentChapter = user.ResetableData.SimRoomData.CurrentChapter;
var buffs = user.ResetableData.SimRoomData.Buffs;
var legacyBuffs = user.ResetableData.SimRoomData.LegacyBuffs;
ResGetSimRoom response = new()
{
@@ -41,20 +43,34 @@ namespace EpinelPS.LobbyServer.Simroom
// NextLegacyBuffResetDate: Resets at 2 AM every Tuesday
NextLegacyBuffResetDate = DateTimeHelper.GetNextWeekdayAtTime("China Standard Time", DayOfWeek.Tuesday, 2).ToTimestamp(),
IsSimpleModeSkipEnabled = user.ResetableData.SimRoomData.IsSimpleModeSkipEnabled,
LastPlayedChapter = new NetSimRoomChapterInfo()
{
Chapter = currentChapter,
Difficulty = CurrentDifficulty,
}
};
// LegacyBuffs
response.LegacyBuffs.AddRange(user.ResetableData.SimRoomData.LegacyBuffs);
response.LegacyBuffs.AddRange(legacyBuffs);
// OverclockData
try
{
response.OverclockData = GetOverclockData(user: user);
}
catch (Exception ex)
{
Logging.WriteLine($"Get OverclockData Exception: {ex.Message}", LogType.Error);
}
// ClearInfos
response.ClearInfos.AddRange(GetClearInfos(user));
// OverclockOptionList
// response.OverclockOptionList.Add([]);
if (user.ResetableData.SimRoomData.CurrentSeasonData.IsOverclock)
{
response.OverclockOptionList.AddRange(user.ResetableData.SimRoomData.CurrentSeasonData.CurrentOptionList);
}
// check if user is in sim room
if (user.ResetableData.SimRoomData.Entered)
@@ -65,15 +81,9 @@ namespace EpinelPS.LobbyServer.Simroom
// 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.Buffs.AddRange(buffs);
response.Buffs.AddRange(legacyBuffs);
// response.NextSimpleModeBuffSelectionInfo = new()
// {
@@ -144,57 +154,66 @@ namespace EpinelPS.LobbyServer.Simroom
public static List<NetSimRoomCharacterHp> GetCharacterHp(User user)
{
List<NetSimRoomCharacterHp> hps = [];
if (user.UserTeams.TryGetValue((int)TeamType.SimulationRoom, out var userTeamData))
var userRemainingHps = user.ResetableData.SimRoomData.RemainingHps;
foreach (var userRemainingHp in userRemainingHps)
{
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 });
}
}
hps.Add(new() { Csn = userRemainingHp.Csn, Hp = userRemainingHp.Hp });
}
return hps;
}
private static NetSimRoomOverclockData GetOverclockData(User user)
{
return new NetSimRoomOverclockData
var currentSeasonData = user.ResetableData.SimRoomData.CurrentSeasonData;
var netOverclockData = new NetSimRoomOverclockData
{
HasClearedLevel50 = currentSeasonData.HasClearedLevel50,
WasInfinitePopupChecked = currentSeasonData.WasInfinitePopupChecked,
WasMainSeasonResetPopupChecked = currentSeasonData.WasMainSeasonResetPopupChecked,
WasSubSeasonResetPopupChecked = currentSeasonData.WasSubSeasonResetPopupChecked,
// CurrentSeasonData
CurrentSeasonData = new NetSimRoomOverclockSeasonData
{
SeasonStartDate = Timestamp.FromDateTimeOffset(DateTime.UtcNow.Date.AddDays(-1)),
SeasonEndDate = Timestamp.FromDateTimeOffset(DateTime.UtcNow.Date.AddDays(7)),
SeasonStartDate = DateTime.UtcNow.Date.AddDays(-2).ToTimestamp(),
SeasonEndDate = DateTime.UtcNow.Date.AddDays(12).ToTimestamp(),
IsSeasonOpen = true,
Season = 1,
SubSeason = 1,
SeasonWeekCount = 1
Season = currentSeasonData.CurrentSeason,
SubSeason = currentSeasonData.CurrentSubSeason,
SeasonWeekCount = 2,
},
// CurrentSeasonHighScore
CurrentSeasonHighScore = new NetSimRoomOverclockHighScoreData
{
CreatedAt = Timestamp.FromDateTimeOffset(DateTime.UtcNow.Date.AddDays(-1)),
OptionLevel = 1,
Season = 1,
SubSeason = 1,
OptionList = { 1 }
CreatedAt = currentSeasonData.CurrentSeasonHighScore.CreatedAt ?? DateTime.UtcNow.Date.AddDays(-2).ToTimestamp(),
Season = currentSeasonData.CurrentSeasonHighScore.Season,
SubSeason = currentSeasonData.CurrentSeasonHighScore.SubSeason,
OptionList = { currentSeasonData.CurrentSeasonHighScore.OptionList },
OptionLevel = currentSeasonData.CurrentSeasonHighScore.OptionLevel,
},
// CurrentSubSeasonHighScore
CurrentSubSeasonHighScore = new NetSimRoomOverclockHighScoreData
{
CreatedAt = Timestamp.FromDateTimeOffset(DateTime.UtcNow.Date.AddDays(-1)),
OptionLevel = 1,
Season = 1,
SubSeason = 1,
OptionList = { 1 }
CreatedAt = currentSeasonData.CurrentSubSeasonHighScore.CreatedAt ?? DateTime.UtcNow.Date.AddDays(-2).ToTimestamp(),
Season = currentSeasonData.CurrentSubSeasonHighScore.Season,
SubSeason = currentSeasonData.CurrentSubSeasonHighScore.SubSeason,
OptionList = { currentSeasonData.CurrentSubSeasonHighScore.OptionList },
OptionLevel = currentSeasonData.CurrentSubSeasonHighScore.OptionLevel,
},
// LatestOption
LatestOption = new NetSimRoomOverclockOptionSettingData
{
Season = 1,
OptionList = { 1 }
Season = currentSeasonData.LatestOption.Season,
OptionList = { currentSeasonData.LatestOption.OptionList },
}
};
netOverclockData.CurrentSeasonData.Season = GameData.Instance.SimulationRoomOcSeasonTable.Keys.Max();
netOverclockData.CurrentSeasonData.SubSeason = 3;
return netOverclockData;
}
}
}

View File

@@ -0,0 +1,22 @@
using EpinelPS.Database;
using EpinelPS.Utils;
namespace EpinelPS.LobbyServer.Simroom
{
[PacketPath("/simroom/popup-check/infinite")]
public class InfinitePopupCheck : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
await ReadData<ReqInfinitePopupCheck>();
var user = GetUser();
ResInfinitePopupCheck response = new();
user.ResetableData.SimRoomData.CurrentSeasonData.WasInfinitePopupChecked = true;
JsonDb.Save();
await WriteDataAsync(response);
}
}
}

View File

@@ -0,0 +1,40 @@
using EpinelPS.Utils;
namespace EpinelPS.LobbyServer.Simroom
{
[PacketPath("/simroom/proceedskipfunction")]
public class ProceedSkipFunction : LobbyMsgHandler
{
protected override async Task HandleAsync()
{
// { "location": { "chapter": 3, "stage": 6, "order": 2 }, "event": 21060, "selectionNumber": 2, "selectionGroupElementId": 210602 }
var req = await ReadData<ReqProceedSimRoomSkipFunction>();
// ReqProceedSimRoomSkipFunction Fields
// NetSimRoomEventLocationInfo location,
// int event,
// int selectionNumber,
// int selectionGroupElementId,
User user = GetUser();
// ResProceedSimRoomSkipFunction Fields
// SimRoomResult Result,
ResProceedSimRoomSkipFunction response = new()
{
Result = SimRoomResult.Success
};
// 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);
await WriteDataAsync(response);
}
}
}

View File

@@ -45,6 +45,7 @@ namespace EpinelPS.LobbyServer.Simroom
user.ResetableData.SimRoomData.Events = [];
user.ResetableData.SimRoomData.RemainingHps = [];
user.ResetableData.SimRoomData.Buffs = [];
user.ResetableData.SimRoomData.CurrentSeasonData.IsOverclock = false;
JsonDb.Save();

View File

@@ -60,6 +60,12 @@ namespace EpinelPS.LobbyServer.Simroom
var difficulty = user.ResetableData.SimRoomData.CurrentDifficulty;
var reward = SimRoomHelper.SimRoomReceivedReward(user, difficulty, location.Chapter);
if (reward is not null) response.Reward = reward;
var overclockReward = SimRoomHelper.SimRoomOverclockReceivedReward(user);
if (overclockReward is not null) response.RewardByOverclock = overclockReward;
// Update User OverclockHighScoreData
SimRoomHelper.UpdateOverclockHighScoreData(user, req.Location);
}
}

View File

@@ -20,9 +20,22 @@ namespace EpinelPS.LobbyServer.Simroom
user.ResetableData.SimRoomData.Entered = true;
user.ResetableData.SimRoomData.CurrentDifficulty = req.Difficulty;
user.ResetableData.SimRoomData.CurrentChapter = req.StartingChapter;
// Update season data
bool isOverclock = req.OverclockOptionList is not null && req.OverclockOptionList.Count > 0 && req.OverclockSeason > 0 && req.OverclockSubSeason > 0;
var currentSeasonData = user.ResetableData.SimRoomData.CurrentSeasonData;
currentSeasonData.IsOverclock = isOverclock;
if (isOverclock)
{
currentSeasonData.CurrentSeason = req.OverclockSeason;
currentSeasonData.CurrentSubSeason = req.OverclockSubSeason;
currentSeasonData.CurrentOptionList = [.. req.OverclockOptionList];
}
user.ResetableData.SimRoomData.CurrentSeasonData = currentSeasonData;
JsonDb.Save();
List<NetSimRoomEvent> events = SimRoomHelper.GetSimRoomEvents(user);
List<NetSimRoomEvent> events = SimRoomHelper.GetSimRoomEvents(user, req.Difficulty, req.StartingChapter,
[.. req.OverclockOptionList], req.OverclockSeason, req.OverclockSubSeason);
user.ResetableData.SimRoomData.Events = [.. events.Select(SimRoomHelper.NetToM)];
JsonDb.Save();

View File

@@ -1,6 +1,7 @@
using EpinelPS.Data;
using EpinelPS.Database;
using EpinelPS.Utils;
using Google.Protobuf.WellKnownTypes;
using log4net;
using Newtonsoft.Json;
@@ -17,15 +18,32 @@ namespace EpinelPS.LobbyServer.Simroom
/// <param name="user"> User </param>
/// <param name="difficultyId"> DifficultyId </param>
/// <param name="chapterId"> ChapterId </param>
/// <param name="overclockOptionList"> OverclockOptionList </param>
/// <param name="overclockSeason"> OverclockSeason </param>
/// <param name="overclockSubSeason"> OverclockSubSeason </param>
/// <returns>The list of NetSimRoomEvent</returns>
public static List<NetSimRoomEvent> GetSimRoomEvents(User user, int difficultyId = 0, int chapterId = 0)
public static List<NetSimRoomEvent> GetSimRoomEvents(User user, int difficultyId = 0, int chapterId = 0,
List<int>? overclockOptionList = null, int overclockSeason = 0, int overclockSubSeason = 0)
{
bool isOverclock = overclockOptionList is not null && overclockOptionList.Count > 0 && overclockSeason > 0 && overclockSubSeason > 0;
List<NetSimRoomEvent> netSimRooms = [];
var currentOCData = user.ResetableData.SimRoomData.CurrentSeasonData;
int currentDifficulty = user.ResetableData.SimRoomData.CurrentDifficulty;
int currentChapter = user.ResetableData.SimRoomData.CurrentChapter;
int currentOCSeason = currentOCData.CurrentSeason;
int currentOCSubSeason = currentOCData.CurrentSubSeason;
List<int> currentOCList = currentOCData.CurrentOptionList;
if (difficultyId > 0) currentDifficulty = difficultyId;
if (chapterId > 0) currentChapter = chapterId;
if (isOverclock)
{
currentOCSeason = overclockSeason;
currentOCSubSeason = overclockSubSeason;
currentOCList = overclockOptionList ?? [];
}
var events = user.ResetableData.SimRoomData.Events;
if (events.Count > 1)
@@ -33,7 +51,8 @@ namespace EpinelPS.LobbyServer.Simroom
return [.. events.Select(MToNet)];
}
var simRoomChapter = GameData.Instance.SimulationRoomChapterTable.Values.FirstOrDefault(x => x.Chapter == currentChapter && x.DifficultyId == currentDifficulty);
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();
@@ -43,13 +62,18 @@ namespace EpinelPS.LobbyServer.Simroom
if (simRoomChapter is null) return netSimRooms;
int battleEventGroup = GetBattleEventGroup(overclockOptionList, isOverclock);
bool isHardBattle = GetIsHardBattle(overclockOptionList, isOverclock);
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 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]);
var simRoomBattleEvent = CreateSimRoomBattleEvent(simRoomChapter, i, 1, simRoomBattleEventRecords, randomBuffPreview[0],
overclockSeason: currentOCSeason, battleEventGroup: battleEventGroup, isOverclock);
events.Add(NetToM(simRoomBattleEvent));
netSimRooms.Add(simRoomBattleEvent);
}
@@ -117,18 +141,26 @@ namespace EpinelPS.LobbyServer.Simroom
{
var battleBuffPreviews = buffPreviewRecords.FindAll(x
=> x.EventType is SimulationRoomEvent.NormalBattle or SimulationRoomEvent.EliteBattle);
if (isOverclock && isHardBattle)
{
battleBuffPreviews = buffPreviewRecords.FindAll(x
=> x.EventType is 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);
var simRoomBattleEvent = CreateSimRoomBattleEvent(chapter: simRoomChapter, stage: i, order: order,
simRoomBattleEventRecords, simRoomBuffPreview, overclockSeason: currentOCSeason, battleEventGroup: battleEventGroup, isOverclock);
events.Add(NetToM(simRoomBattleEvent));
netSimRooms.Add(simRoomBattleEvent);
}
}
}
// user.AddTrigger()
user.AddTrigger(Trigger.SimulationRoomStart, value: 1);
user.AddTrigger(Trigger.SimulationRoomClearCount1Only, value: 1);
user.AddTrigger(Trigger.SimulationRoomClearWithoutCondition, value: 1);
user.ResetableData.SimRoomData.Events = events;
JsonDb.Save();
return netSimRooms;
@@ -264,7 +296,8 @@ namespace EpinelPS.LobbyServer.Simroom
/// <param name="randomBuffPreview"></param>
/// <returns>NetSimRoomEvent</returns>
private static NetSimRoomEvent CreateSimRoomBattleEvent(SimulationRoomChapterRecord chapter, int stage, int order,
List<SimulationRoomBattleEventRecord> simRoomBattleEventRecords, SimulationRoomBuffPreviewRecord randomBuffPreview)
List<SimulationRoomBattleEventRecord> simRoomBattleEventRecords, SimulationRoomBuffPreviewRecord randomBuffPreview,
int overclockSeason = 0, int battleEventGroup = 0, bool isOverclock = false)
{
var simRoomEvent = new NetSimRoomEvent();
var location = new NetSimRoomEventLocationInfo();
@@ -273,8 +306,17 @@ namespace EpinelPS.LobbyServer.Simroom
location.Chapter = chapter.Chapter;
location.Stage = stage;
location.Order = order;
var simRoomBuffPreviewBattleEvents = simRoomBattleEventRecords.FindAll(x
=> x.EventType == randomBuffPreview.EventType && x.DifficultyConditionValue <= chapter.DifficultyId);
var simRoomBuffPreviewBattleEvents = simRoomBattleEventRecords.FindAll(x =>
x.EventType == randomBuffPreview.EventType
&& x.DifficultyConditionValue <= chapter.DifficultyId
&& x.UseOcMode == false);
if (isOverclock)
{
simRoomBuffPreviewBattleEvents = simRoomBattleEventRecords.FindAll(x =>
x.EventType == randomBuffPreview.EventType
&& x.UseOcMode == true && x.UseSeasonId == overclockSeason
&& x.BattleEventGroup == battleEventGroup);
}
log.Debug($"EventType: {randomBuffPreview.EventType}, SimRoomBattleEventRecord Count: {simRoomBuffPreviewBattleEvents.Count}");
var randomBattleEvents = GetRandomItems(simRoomBuffPreviewBattleEvents, 1);
foreach (var battleEvent in randomBattleEvents)
@@ -428,10 +470,42 @@ namespace EpinelPS.LobbyServer.Simroom
}
}
public static int GetBattleEventGroup(List<int>? overclockOptionList = null, bool isOverclock = false)
{
// TODO: Implement battle event group logic
if (!isOverclock) return 0;
if (overclockOptionList is null || overclockOptionList.Count == 0) return 0;
var simRoomOcOptionRecords = GameData.Instance.SimulationRoomOcOptionTable.Values.Where(x =>
overclockOptionList.Contains(x.Id)).ToList();
if (simRoomOcOptionRecords is null || simRoomOcOptionRecords.Count == 0) return 0;
foreach (var item in simRoomOcOptionRecords.FindAll(x => x.OptionData.Count > 0))
{
var optionData = item.OptionData.Find(x => x.OptionFunction == SimulationRoomOcOptionFunction.BattleEventGroupChange);
if (optionData is not null) return optionData.OptionValue;
}
return 0;
}
public static bool GetIsHardBattle(List<int>? overclockOptionList = null, bool isOverclock = false)
{
// TODO: Implement hard battle check logic
if (!isOverclock) return false;
if (overclockOptionList is null || overclockOptionList.Count == 0) return false;
var simRoomOcOptionRecords = GameData.Instance.SimulationRoomOcOptionTable.Values.Where(x =>
overclockOptionList.Contains(x.Id)).ToList();
if (simRoomOcOptionRecords is null || simRoomOcOptionRecords.Count == 0) return false;
foreach (var item in simRoomOcOptionRecords)
{
if (item.OptionNameLocalkey.EndsWith("_HARD_BATTLE_ONLY")) return true;
}
return false;
}
/// <summary>
/// Update User SimRoomEvent Events
/// </summary>
public static void UpdateUserSimRoomEvent(User user, int index, List<SimRoomEvent> events, int selectionNumber = 0,
public static void UpdateUserSimRoomEvent(User user, int index, List<SimRoomEvent> events,
int selectionNumber = 0, int selectionGroupElementId = 0,
bool isDone = false, int battleProgress = 0, List<int>? BuffOptions = null)
{
try
@@ -441,7 +515,7 @@ namespace EpinelPS.LobbyServer.Simroom
simRoomEvent.Selected = true;
// Update Selection Group
var groupIndex = simRoomEvent.Selection.Group.FindIndex(x => x.SelectionNumber == selectionNumber);
var groupIndex = simRoomEvent.Selection.Group.FindIndex(x => x.Id == selectionGroupElementId || x.SelectionNumber == selectionNumber);
if (groupIndex > -1 && isDone)
{
var group = simRoomEvent.Selection.Group[groupIndex];
@@ -472,100 +546,192 @@ namespace EpinelPS.LobbyServer.Simroom
}
}
public static List<SimRoomCharacterHp> UpdateUserRemainingHps(User user, List<NetSimRoomCharacterHp>? remainingHps = null, int teamNumber = 1, int HpValue = 10000)
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)
var userRemainingHps = user.ResetableData.SimRoomData.RemainingHps ?? [];
// Update from input parameters
if (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 });
}
}
userRemainingHps = UpdateFromRemainingHps(userRemainingHps, remainingHps);
}
// 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
// Initialize team HPs
userRemainingHps = InitializeTeamHps(user, userRemainingHps, teamNumber, hpValue);
// Save and return
user.ResetableData.SimRoomData.RemainingHps = userRemainingHps;
return userRemainingHps;
}
catch (Exception e)
{
log.Error($"Update UserRemainingHps Exception: {e.Message}");
log.Error($"Stack Trace: {e.StackTrace}");
return user.ResetableData.SimRoomData.RemainingHps ?? [];
}
}
private static List<SimRoomCharacterHp> UpdateFromRemainingHps(List<SimRoomCharacterHp> userRemainingHps, List<NetSimRoomCharacterHp> remainingHps)
{
var existingHpsDict = userRemainingHps.ToDictionary(x => x.Csn, x => x);
foreach (var characterHp in remainingHps)
{
existingHpsDict[characterHp.Csn] = new SimRoomCharacterHp { Csn = characterHp.Csn, Hp = characterHp.Hp };
}
return [.. existingHpsDict.Values];
}
private static List<SimRoomCharacterHp> InitializeTeamHps(User user, List<SimRoomCharacterHp> userRemainingHps, int teamNumber, int hpValue)
{
if (!user.UserTeams.TryGetValue((int)TeamType.SimulationRoom, out var userTeam))
return userRemainingHps;
var team = userTeam.Teams.FirstOrDefault(x => x.TeamNumber == teamNumber);
if (team == null)
return userRemainingHps;
var existingHpsDict = userRemainingHps.ToDictionary(x => x.Csn, x => x);
foreach (var slot in team.Slots)
{
if (!existingHpsDict.ContainsKey(slot.Value))
{
existingHpsDict[slot.Value] = new SimRoomCharacterHp { Csn = slot.Value, Hp = hpValue };
}
}
return [.. existingHpsDict.Values];
}
/// <summary>
/// Received Reward
/// </summary>
/// <param name="user">User</param>
/// <param name="difficultyId">int difficultyId</param>
/// <param name="chapterId">int chapterId</param>
/// <returns>NetRewardData</returns>
public static NetRewardData? SimRoomReceivedReward(User user, int difficultyId, int chapterId)
{
// check if reward is received
NetRewardData? reward = null;
if (!IsRewardReceived(user, difficultyId, chapterId))
{
// get all chapter records for the current difficulty
var chapterRecords = GameData.Instance.SimulationRoomChapterTable.Values
.Where(x => x.DifficultyId <= difficultyId);
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)
if (chapterRecords.Any())
{
var receivedRewardChapters = user.ResetableData.SimRoomData.ReceivedRewardChapters;
// get last received reward chapter
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);
}
.OrderBy(x => x.Difficulty).ThenBy(x => x.Chapter).LastOrDefault();
var receivedRewardChapterId = receivedRewardChapter?.Chapter ?? 0;
var receivedRewardDifficultyId = receivedRewardChapter?.Difficulty ?? 0;
reward = new NetRewardData();
if (receivedRewardChapterRecord is null)
//
bool shouldAddRewardChapter = false;
List<Reward_Data> IncrementalRewards = [];
foreach (var chapter in chapterRecords)
{
bool shouldCalculateReward = chapter.DifficultyId > receivedRewardDifficultyId ||
(chapter.DifficultyId == receivedRewardDifficultyId && chapter.Chapter > receivedRewardChapterId);
if (shouldCalculateReward)
{
CalculateIncrementalReward(ref IncrementalRewards, chapter.RewardId);
}
if (chapter.Chapter == chapterId && chapter.DifficultyId == difficultyId)
{
shouldAddRewardChapter = true;
}
}
if (shouldAddRewardChapter)
{
// 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)
foreach (var item in IncrementalRewards)
{
// Claiming the reward for a higher chapter
reward = CalculateIncrementalReward(user, chapter, receivedRewardChapterRecord);
AddRewardChapter(user, difficultyId, chapterId);
RewardUtils.AddSingleObject(user, ref reward, item.RewardId, item.RewardType, item.RewardValue);
}
// Check if the received reward chapter is lower than the current difficulty
else if (receivedRewardChapterRecord.DifficultyId < difficultyId)
}
}
return reward;
}
/// <summary>
/// Overclock Received Reward
/// </summary>
/// <param name="user">User</param>
/// <returns>NetRewardData</returns>
public static NetRewardData? SimRoomOverclockReceivedReward(User user)
{
// Claiming the reward for a higher difficulty
reward = RewardUtils.RegisterRewardsForUser(user, rewardId: chapter.RewardId);
AddRewardChapter(user, difficultyId, chapterId);
// check if reward is received
NetRewardData? reward = null;
var currentSeasonData = user.ResetableData.SimRoomData.CurrentSeasonData;
if (currentSeasonData is null) return null;
if (!currentSeasonData.IsOverclock) return null;
var currentOptionList = currentSeasonData.CurrentOptionList;
if (currentOptionList is null || currentOptionList.Count == 0) return null;
var lastHighOption = currentSeasonData.CurrentSeasonHighScore;
var lastOptionLevel = lastHighOption?.OptionLevel ?? 0;
var currentSeasonRecord = GameData.Instance.SimulationRoomOcSeasonTable.Values
.FirstOrDefault(x => x.Id == currentSeasonData.CurrentSeason);
if (currentSeasonRecord is null) return null;
var currentOptionRecords = GameData.Instance.SimulationRoomOcOptionTable.Values
.Where(x => currentOptionList.Contains(x.Id)).ToList();
if (currentOptionRecords.Count == 0) return null;
var currentOptionLevel = currentOptionRecords.Max(x => x.OptionOverclockLevel);
// Update User Latest Option
if (currentOptionLevel > 0)
{
user.ResetableData.SimRoomData.CurrentSeasonData.LatestOption = new OverclockHighScoreData()
{
Season = currentSeasonData.CurrentSeason,
OptionLevel = currentOptionLevel,
OptionList = currentOptionList
};
}
if (currentOptionLevel <= lastOptionLevel) return null;
if (currentSeasonRecord.OverclockLevelGroup <= 0) return null;
// get all oc level records for the current season and option level
var ocLevelRecords = GameData.Instance.SimulationRoomOcLevelTable.Values
.Where(x => x.GroupId == currentSeasonRecord.OverclockLevelGroup
&& x.OverclockLevel <= currentOptionLevel
&& x.OverclockLevel > lastOptionLevel
&& x.RewardId > 0).ToList();
if (ocLevelRecords == null || ocLevelRecords.Count == 0) return null;
reward = new NetRewardData();
List<Reward_Data> IncrementalRewards = [];
foreach (var ocLevel in ocLevelRecords)
{
CalculateIncrementalReward(ref IncrementalRewards, ocLevel.RewardId);
}
foreach (var item in IncrementalRewards)
{
RewardUtils.AddSingleObject(user, ref reward, item.RewardId, item.RewardType, item.RewardValue);
}
return reward;
}
@@ -580,6 +746,7 @@ namespace EpinelPS.LobbyServer.Simroom
log.Debug($"IsRewardReceived (diff: {difficultyId}, chapter: {chapterId}): {isReceived}");
return isReceived;
}
/// <summary>
/// Add reward chapter
/// </summary>
@@ -595,39 +762,119 @@ namespace EpinelPS.LobbyServer.Simroom
/// <summary>
/// Calculate incremental reward
/// </summary>
/// <returns>Incremental reward data</returns>
private static NetRewardData CalculateIncrementalReward(User user,
SimulationRoomChapterRecord chapter, SimulationRoomChapterRecord receivedChapterRecord)
private static void CalculateIncrementalReward(ref List<Reward_Data> IncrementalRewards, int rewardId)
{
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)
var rewardRecord = GameData.Instance.GetRewardTableEntry(rewardId);
if (rewardRecord is not null && rewardRecord.Rewards.Count > 0)
{
// If rewardRecord or receivedRewardRecord is empty, return the complete reward record
reward = RewardUtils.RegisterRewardsForUser(user, rewardId: chapter.RewardId);
foreach (var rewardItem in rewardRecord.Rewards.Where(x => x is not null && x.RewardValue > 0))
{
// If reward already exists, increment its value, otherwise add it to the list
var rewardIndex = IncrementalRewards.FindIndex(x => x.RewardId == rewardItem.RewardId);
if (rewardIndex > -1)
{
IncrementalRewards[rewardIndex].RewardValue = IncrementalRewards[rewardIndex].RewardValue + rewardItem.RewardValue;
}
else
{
var receivedRewardIds = receivedRewardRecord.Rewards
.Where(x => x != null)
.ToDictionary(x => x.RewardId, x => x.RewardValue);
IncrementalRewards.Add(rewardItem);
}
}
}
}
foreach (var rewardItem in rewardRecord.Rewards.Where(x => x != null))
public static void UpdateOverclockHighScoreData(User user, NetSimRoomEventLocationInfo location)
{
int receivedAmount = receivedRewardIds.GetValueOrDefault(rewardItem.RewardId, 0);
int remainingAmount = Math.Max(0, rewardItem.RewardValue - receivedAmount);
var currentSeasonData = user.ResetableData.SimRoomData.CurrentSeasonData;
if (remainingAmount > 0)
// Check if current season is overclock, if not, quit
if (currentSeasonData is null || !currentSeasonData.IsOverclock)
{
RewardUtils.AddSingleObject(user, ref reward, rewardItem.RewardId,
rewardType: rewardItem.RewardType, remainingAmount);
}
}
return;
}
return reward;
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];
// TODO: This is a temporary solution, need to find a better way to determine if the challenge is completed
var maxStage = events.Max(e => e.Location.Stage);
if (simRoomEvent.Location.Stage == maxStage)
{
var currentOptionList = currentSeasonData.CurrentOptionList;
if (currentOptionList.Count <= 0)
{
// If currentOptionList is empty, quit
return;
}
var ocOptionTable = GameData.Instance.SimulationRoomOcOptionTable.Values.ToList();
var currentOptions = ocOptionTable.Where(x => currentOptionList.Contains(x.Id)).ToList();
int currentOptionLevel = currentOptions.Sum(x => x.OptionOverclockLevel);
var highScoreData = CreateOrUpdateHighScoreData(currentSeasonData, currentOptionList, currentOptionLevel, ocOptionTable);
if (highScoreData is null) return; // If highScoreData is null, quit
if (currentOptionLevel >= 50)
{
currentSeasonData.HasClearedLevel50 = true;
}
// Update User CurrentSeasonData
highScoreData.CreatedAt = DateTime.UtcNow.Date.ToTimestamp();
currentSeasonData.CurrentSeasonHighScore = highScoreData;
currentSeasonData.CurrentSubSeasonHighScore = highScoreData;
user.ResetableData.SimRoomData.CurrentSeasonData = currentSeasonData;
}
}
else
{
log.Warn($"Not Found User.ResetableData.SimRoomData.Events");
}
}
private static OverclockHighScoreData? CreateOrUpdateHighScoreData(
OverclockData currentSeasonData, List<int> currentOptionList,
int currentOptionLevel, List<SimulationRoomOverclockOptionRecord> ocOptionTable)
{
var currentHighOptionList = currentSeasonData.CurrentSeasonHighScore.OptionList;
// If currentHighOptionList is empty, return new HighScoreData
if (currentHighOptionList.Count <= 0)
{
return new OverclockHighScoreData
{
Season = currentSeasonData.CurrentSeason,
SubSeason = currentSeasonData.CurrentSubSeason,
OptionList = currentOptionList,
OptionLevel = currentOptionLevel
};
}
// Get current high options, current high option level
var currentHighOptions = ocOptionTable.Where(x => currentHighOptionList.Contains(x.Id));
int currentHighOptionLevel = currentHighOptions.Sum(x => x.OptionOverclockLevel);
// If current option level is greater than current high option level, return new HighScoreData
if (currentOptionLevel >= currentHighOptionLevel)
{
return new OverclockHighScoreData
{
Season = currentSeasonData.CurrentSeason,
SubSeason = currentSeasonData.CurrentSubSeason,
OptionList = currentOptionList,
OptionLevel = currentOptionLevel
};
}
return null;
}
}
}

View File

@@ -1,5 +1,4 @@
using EpinelPS.Data;
using EpinelPS.Database;
using EpinelPS.Database;
using EpinelPS.Utils;
namespace EpinelPS.LobbyServer.Team
@@ -21,9 +20,10 @@ namespace EpinelPS.LobbyServer.Team
// Add team data to user data
int contentsId = req.ContentsId + 1; // Default to 1 if not provided
if (req.Type == (int)TeamType.StoryEvent)
if (req.Teams.Count != 0)
{
contentsId = 1; // Default to 1 for story event teams
contentsId = req.Teams.Select(x => x.TeamNumber).Max(x => x);
}
NetUserTeamData teamData = new() { LastContentsTeamNumber = contentsId, Type = req.Type };

View File

@@ -38,6 +38,7 @@ namespace EpinelPS.Models
public int Skill1Lvl = 1;
public int Skill2Lvl = 1;
public int Grade = 0;
public bool IsMainForce = false;
}
public class MainQuestData
{
@@ -128,6 +129,29 @@ namespace EpinelPS.Models
public List<SimRoomChapterInfo> ReceivedRewardChapters = [];
public bool IsSimpleModeSkipEnabled = false;
public bool Entered = false;
public OverclockData CurrentSeasonData = new();
}
public class OverclockData
{
public int CurrentSeason;
public int CurrentSubSeason;
public List<int> CurrentOptionList = [];
public bool IsOverclock = false;
public bool HasClearedLevel50 = false;
public bool WasInfinitePopupChecked = false;
public bool WasMainSeasonResetPopupChecked = true;
public bool WasSubSeasonResetPopupChecked = true;
public OverclockHighScoreData CurrentSeasonHighScore = new();
public OverclockHighScoreData CurrentSubSeasonHighScore = new();
public OverclockHighScoreData LatestOption = new();
}
public class OverclockHighScoreData
{
public int Season;
public int SubSeason;
public List<int> OptionList = [];
public int OptionLevel;
public Google.Protobuf.WellKnownTypes.Timestamp? CreatedAt;
}
public class SimRoomEvent
{

View File

@@ -460,7 +460,10 @@ public class User
{
SimRoomData = new()
{
LegacyBuffs = ResetableData.SimRoomData.LegacyBuffs // Retain old LegacyBuffs data
LegacyBuffs = ResetableData.SimRoomData.LegacyBuffs, // Retain old LegacyBuffs data
CurrentDifficulty = ResetableData.SimRoomData.CurrentDifficulty,
CurrentChapter = ResetableData.SimRoomData.CurrentChapter,
CurrentSeasonData = ResetableData.SimRoomData.CurrentSeasonData,
}
};
needsSave = true;
@@ -472,7 +475,18 @@ public class User
Logging.WriteLine("Resetting weekly user data...", LogType.Warning);
LastWeeklyReset = DateTime.UtcNow;
ResetableData = new();
var currentSeasonData = ResetableData.SimRoomData.CurrentSeasonData;
currentSeasonData.LatestOption = new();
ResetableData = new()
{
SimRoomData = new()
{
// Retain old LegacyBuffs data and currentSeason data
CurrentDifficulty = ResetableData.SimRoomData.CurrentDifficulty,
CurrentChapter = ResetableData.SimRoomData.CurrentChapter,
CurrentSeasonData = currentSeasonData,
}
};
needsSave = true;
}