mirror of
https://github.com/EpinelPS/EpinelPS.git
synced 2025-12-12 06:54:34 +01:00
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:
@@ -25,4 +25,7 @@ dotnet_diagnostic.CS8603.severity = none
|
||||
dotnet_diagnostic.SYSLIB0039.severity = none
|
||||
|
||||
# CS0219: 变量已被赋值,但从未使用过它的值
|
||||
dotnet_diagnostic.CS0219.severity = none
|
||||
dotnet_diagnostic.CS0219.severity = none
|
||||
|
||||
# CS8618: 类型不包含 null 值的属性
|
||||
dotnet_diagnostic.CS8618.severity = none
|
||||
@@ -308,6 +308,16 @@ namespace EpinelPS.Data
|
||||
public readonly Dictionary<int, SimulationRoomBuffPreviewRecord> SimulationRoomBuffPreviewTable = [];
|
||||
[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()
|
||||
{
|
||||
@@ -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];
|
||||
|
||||
@@ -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)];
|
||||
|
||||
29
EpinelPS/LobbyServer/Character/SetCharacterMainForce.cs
Normal file
29
EpinelPS/LobbyServer/Character/SetCharacterMainForce.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using EpinelPS.Utils;
|
||||
using EpinelPS.Data;
|
||||
using EpinelPS.Utils;
|
||||
|
||||
namespace EpinelPS.LobbyServer.LobbyUser
|
||||
{
|
||||
@@ -7,12 +8,12 @@ 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
|
||||
|
||||
List<int> specialStages = [6003003, 6002008, 6002016, 6005003, 6003021, 6011018, 6007021, 6004018, 6005013, 6003009, 6003012, 6009017, 6016039, 6001004, 6000003, 6000001, 6002001, 6004023, 6005026, 6020050, 6006004, 6006023,6022049];
|
||||
List<int> specialStages = [6003003, 6002008, 6002016, 6005003, 6003021, 6011018, 6007021, 6004018, 6005013, 6003009, 6003012, 6009017, 6016039, 6001004, 6000003, 6000001, 6002001, 6004023, 6005026, 6020050, 6006004, 6006023, 6022049];
|
||||
|
||||
ResGetContentsOpenData response = new();
|
||||
foreach (FieldInfoNew field in user.FieldInfoNew.Values)
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
44
EpinelPS/LobbyServer/Simroom/GetAcquireBuffFunction.cs
Normal file
44
EpinelPS/LobbyServer/Simroom/GetAcquireBuffFunction.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
response.OverclockData = GetOverclockData(user: user);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
22
EpinelPS/LobbyServer/Simroom/InfinitePopupCheck.cs
Normal file
22
EpinelPS/LobbyServer/Simroom/InfinitePopupCheck.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
40
EpinelPS/LobbyServer/Simroom/ProceedSkipFunction.cs
Normal file
40
EpinelPS/LobbyServer/Simroom/ProceedSkipFunction.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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,104 +546,196 @@ 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 ?? [];
|
||||
}
|
||||
return userRemainingHps;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
// 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);
|
||||
RewardUtils.AddSingleObject(user, ref reward, item.RewardId, item.RewardType, item.RewardValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
return reward;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overclock Received Reward
|
||||
/// </summary>
|
||||
/// <param name="user">User</param>
|
||||
/// <returns>NetRewardData</returns>
|
||||
public static NetRewardData? SimRoomOverclockReceivedReward(User user)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if reward is received
|
||||
/// </summary>
|
||||
@@ -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);
|
||||
}
|
||||
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))
|
||||
foreach (var rewardItem in rewardRecord.Rewards.Where(x => x is not null && x.RewardValue > 0))
|
||||
{
|
||||
int receivedAmount = receivedRewardIds.GetValueOrDefault(rewardItem.RewardId, 0);
|
||||
int remainingAmount = Math.Max(0, rewardItem.RewardValue - receivedAmount);
|
||||
|
||||
if (remainingAmount > 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)
|
||||
{
|
||||
RewardUtils.AddSingleObject(user, ref reward, rewardItem.RewardId,
|
||||
rewardType: rewardItem.RewardType, remainingAmount);
|
||||
IncrementalRewards[rewardIndex].RewardValue = IncrementalRewards[rewardIndex].RewardValue + rewardItem.RewardValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
IncrementalRewards.Add(rewardItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return reward;
|
||||
}
|
||||
|
||||
public static void UpdateOverclockHighScoreData(User user, NetSimRoomEventLocationInfo location)
|
||||
{
|
||||
var currentSeasonData = user.ResetableData.SimRoomData.CurrentSeasonData;
|
||||
|
||||
// Check if current season is overclock, if not, quit
|
||||
if (currentSeasonData is null || !currentSeasonData.IsOverclock)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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 };
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user