diff --git a/EpinelPS/Database/JsonDb.cs b/EpinelPS/Database/JsonDb.cs index 7a944b1..ab11034 100644 --- a/EpinelPS/Database/JsonDb.cs +++ b/EpinelPS/Database/JsonDb.cs @@ -3,6 +3,8 @@ using EpinelPS.LobbyServer; using EpinelPS.StaticInfo; using EpinelPS.Utils; using Swan.Logging; +using Google.Protobuf.WellKnownTypes; +using static Google.Rpc.Context.AttributeContext.Types; namespace EpinelPS.Database { @@ -64,6 +66,21 @@ namespace EpinelPS.Database public List CompletedScenarios = new(); } + public class SynchroSlot + { + /// + /// Index of slot, 1 based + /// + public int Slot; + /// + /// Character CSN + /// + public long CharacterSerialNumber; + /// + /// Time when slot cooldown expires + /// + public long AvailableAt; + } public class User { // User info @@ -94,6 +111,9 @@ namespace EpinelPS.Database public Dictionary Currency = new() { { CurrencyType.ContentStamina, 2 } }; + public List SynchroSlots = new List(); + public bool SynchroDeviceUpgraded = false; + public int SynchroDeviceLevel = 200; public List Items = new(); public List Characters = []; @@ -219,6 +239,45 @@ namespace EpinelPS.Database { return Characters.Where(x => x.Csn == value).FirstOrDefault(); } + + internal bool GetSynchro(long csn) + { + return SynchroSlots.Where(x => x.CharacterSerialNumber == csn).Count() >= 1; + } + internal int GetCharacterLevel(int csn) + { + var c = GetCharacterBySerialNumber(csn); + if (c == null) throw new Exception("failed to lookup character"); + + return GetCharacterLevel(csn, c.Level); + } + internal int GetCharacterLevel(int csn, int characterLevel) + { + foreach (var item in SynchroSlots) + { + if (item.CharacterSerialNumber == csn) + { + return GetSynchroLevel(); + } + } + return characterLevel; + } + internal int GetSynchroLevel() + { + if (SynchroDeviceUpgraded) + return SynchroDeviceLevel; + var highestLevelCharacters = Characters.OrderByDescending(x => x.Level).Take(5).ToList(); + + + if (highestLevelCharacters.Count > 0) + { + return highestLevelCharacters.Last().Level; + } + else + { + return 1; + } + } } public class CoreInfo { diff --git a/EpinelPS/LobbyServer/Msgs/Character/ChangeSynchroDevice.cs b/EpinelPS/LobbyServer/Msgs/Character/ChangeSynchroDevice.cs new file mode 100644 index 0000000..c923aa0 --- /dev/null +++ b/EpinelPS/LobbyServer/Msgs/Character/ChangeSynchroDevice.cs @@ -0,0 +1,54 @@ +using EpinelPS.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EpinelPS.LobbyServer.Msgs.Character +{ + [PacketPath("/character/SynchroDevice/Change")] + public class ChangeSynchroDevice : LobbyMsgHandler + { + protected override async Task HandleAsync() + { + var req = await ReadData(); + var user = GetUser(); + + var response = new ResSynchroChange(); + + var highestLevelCharacters = user.Characters.OrderByDescending(x => x.Level).Take(5).ToList(); + + int slot = 1; + foreach (var item in highestLevelCharacters) + { + if (item.Level != 200) + { + throw new Exception("expected level to be 200"); + } + + response.Characters.Add(new NetUserCharacterData() { Default = new() { Csn = item.Csn, Skill1Lv = item.Skill1Lvl, Skill2Lv = item.Skill2Lvl, CostumeId = item.CostumeId, Level = item.Level, Grade = item.Grade, Tid = item.Tid, UltiSkillLv = item.UltimateLevel }, IsSynchro = user.GetSynchro(item.Csn) }); + + + + foreach (var s in user.SynchroSlots) + { + if (s.Slot == slot) + { + s.CharacterSerialNumber = item.Csn; + break; + } + } + slot++; + } + + user.SynchroDeviceUpgraded = true; + + foreach (var item in user.SynchroSlots) + { + response.Slots.Add(new NetSynchroSlot() { Slot = item.Slot, AvailableRegisterAt = item.AvailableAt, Csn = item.CharacterSerialNumber }); + } + await WriteDataAsync(response); + } + } +} diff --git a/EpinelPS/LobbyServer/Msgs/Character/GetCharacterData.cs b/EpinelPS/LobbyServer/Msgs/Character/GetCharacterData.cs index 1c2c5a1..0898f18 100644 --- a/EpinelPS/LobbyServer/Msgs/Character/GetCharacterData.cs +++ b/EpinelPS/LobbyServer/Msgs/Character/GetCharacterData.cs @@ -11,10 +11,10 @@ namespace EpinelPS.LobbyServer.Msgs.Character var user = GetUser(); var response = new ResGetCharacterData(); - // TODO: When Squad view opens in the game, or this request is sent, all character levels reset to 1 as well as cusume IDs + // TODO: When Squad view opens in the game, or this request is sent, all character levels reset to 1 as well as costume IDs //foreach (var item in user.Characters) //{ - // response.Character.Add(new NetUserCharacterData() { Default = new() { Csn = item.Csn, Skill1Lv = item.Skill1Lvl, Skill2Lv = item.Skill2Lvl, CostumeId = item.CostumeId, Lv = item.Level, Grade = item.Grade, Tid = item.Tid, UltiSkillLv = item.UltimateLevel }, IsSynchro = false, Artifact = 0 }); + // response.Character.Add(new NetUserCharacterData() { Default = new() { Csn = item.Csn, Skill1Lv = item.Skill1Lvl, Skill2Lv = item.Skill2Lvl, CostumeId = item.CostumeId, Level = user.GetCharacterLevel(item.Csn, item.Level), Grade = item.Grade, Tid = item.Tid, UltiSkillLv = item.UltimateLevel }, IsSynchro = user.GetSynchro(item.Csn) }); //} var highestLevelCharacters = user.Characters.OrderByDescending(x => x.Level).Take(5).ToList(); diff --git a/EpinelPS/LobbyServer/Msgs/Character/GetSynchrodevice.cs b/EpinelPS/LobbyServer/Msgs/Character/GetSynchrodevice.cs index 229c7ca..5e7d100 100644 --- a/EpinelPS/LobbyServer/Msgs/Character/GetSynchrodevice.cs +++ b/EpinelPS/LobbyServer/Msgs/Character/GetSynchrodevice.cs @@ -1,4 +1,5 @@ -using EpinelPS.Utils; +using EpinelPS.Database; +using EpinelPS.Utils; namespace EpinelPS.LobbyServer.Msgs.Character { @@ -10,6 +11,24 @@ namespace EpinelPS.LobbyServer.Msgs.Character var req = await ReadData(); var user = GetUser(); + if (user.SynchroSlots.Count == 0) + { + + user.SynchroSlots = new() { + new SynchroSlot() { Slot = 1 }, + new SynchroSlot() { Slot = 2}, + new SynchroSlot() { Slot = 3 }, + new SynchroSlot() { Slot = 4 }, + new SynchroSlot() { Slot = 5 }, + + new SynchroSlot() { Slot = 6 }, + new SynchroSlot() { Slot = 7 }, + new SynchroSlot() { Slot = 8 }, + new SynchroSlot() { Slot = 9 }, + new SynchroSlot() { Slot = 10 }, + }; + } + var highestLevelCharacters = user.Characters.OrderByDescending(x => x.Level).Take(5).ToList(); var response = new ResGetSynchroData(); @@ -17,26 +36,18 @@ namespace EpinelPS.LobbyServer.Msgs.Character foreach (var item in highestLevelCharacters) { - response.Synchro.StandardCharacters.Add(new NetUserCharacterData() { Default = new() { Csn = item.Csn, Skill1Lv = item.Skill1Lvl, Skill2Lv = item.Skill2Lvl, CostumeId = item.CostumeId, Level = item.Level, Grade = item.Grade, Tid = item.Tid, UltiSkillLv = item.UltimateLevel } }); + response.Synchro.StandardCharacters.Add(new NetUserCharacterData() { Default = new() { Csn = item.Csn, Skill1Lv = item.Skill1Lvl, Skill2Lv = item.Skill2Lvl, CostumeId = item.CostumeId, Level = item.Level, Grade = item.Grade, Tid = item.Tid, UltiSkillLv = item.UltimateLevel }, IsSynchro = user.GetSynchro(item.Csn) }); } - response.Synchro.Slots.Add(new NetSynchroSlot() { Slot = 1 }); - response.Synchro.Slots.Add(new NetSynchroSlot() { Slot = 2 }); - response.Synchro.Slots.Add(new NetSynchroSlot() { Slot = 3 }); - response.Synchro.Slots.Add(new NetSynchroSlot() { Slot = 4 }); - response.Synchro.Slots.Add(new NetSynchroSlot() { Slot = 5 }); - - if (highestLevelCharacters.Count > 0) + foreach (var item in user.SynchroSlots) { - response.Synchro.SynchroMaxLv = highestLevelCharacters.First().Level; - response.Synchro.SynchroLv = highestLevelCharacters.Last().Level; - } - else - { - response.Synchro.SynchroLv = 1; + response.Synchro.Slots.Add(new NetSynchroSlot() { Slot = item.Slot, AvailableRegisterAt = 1, Csn = item.CharacterSerialNumber }); } - // TODO: Validate response from real server and pull info from user info + response.Synchro.SynchroMaxLv = 1000; + response.Synchro.SynchroLv = user.GetSynchroLevel(); + response.Synchro.IsChanged = user.SynchroDeviceUpgraded; + await WriteDataAsync(response); } } diff --git a/EpinelPS/LobbyServer/Msgs/Character/LevelUp.cs b/EpinelPS/LobbyServer/Msgs/Character/LevelUp.cs index 5c77c6c..1938eed 100644 --- a/EpinelPS/LobbyServer/Msgs/Character/LevelUp.cs +++ b/EpinelPS/LobbyServer/Msgs/Character/LevelUp.cs @@ -59,7 +59,7 @@ namespace EpinelPS.LobbyServer.Msgs.Character }; var highestLevelCharacters = user.Characters.OrderByDescending(x => x.Level).Take(5).ToList(); - response.SynchroLv = highestLevelCharacters.Last().Level; + response.SynchroLv = user.GetSynchroLevel(); foreach (var c in highestLevelCharacters) { diff --git a/EpinelPS/LobbyServer/Msgs/Character/RegisterSynchroDevice.cs b/EpinelPS/LobbyServer/Msgs/Character/RegisterSynchroDevice.cs new file mode 100644 index 0000000..117fcbd --- /dev/null +++ b/EpinelPS/LobbyServer/Msgs/Character/RegisterSynchroDevice.cs @@ -0,0 +1,54 @@ +using EpinelPS.Utils; +using Swan.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EpinelPS.LobbyServer.Msgs.Character +{ + [PacketPath("/character/SynchroDevice/Regist")] + public class RegisterSynchroDevice : LobbyMsgHandler + { + protected override async Task HandleAsync() + { + var req = await ReadData(); + var user = GetUser(); + var targetCharacter = user.GetCharacterBySerialNumber(req.Csn); + if (targetCharacter == null) throw new Exception("target character does not exist"); + + var response = new ResSynchroRegister(); + foreach (var item in user.SynchroSlots) + { + if (item.Slot == req.Slot) + { + if (item.CharacterSerialNumber != 0) + { + Logger.Warn("must remove character from synchrodevice first"); + } + else + { + item.CharacterSerialNumber = req.Csn; + response.IsSynchro = true; + response.Character = new NetUserCharacterDefaultData() + { + Csn = item.CharacterSerialNumber, + CostumeId = targetCharacter.CostumeId, + Grade = targetCharacter.Grade, + Level = user.GetSynchroLevel(), + Skill1Lv = targetCharacter.Skill1Lvl, + Skill2Lv = targetCharacter.Skill2Lvl, + Tid = targetCharacter.Tid, + UltiSkillLv = targetCharacter.UltimateLevel + }; + response.Slot = new NetSynchroSlot() { AvailableRegisterAt = item.AvailableAt, Csn = item.CharacterSerialNumber, Slot = item.Slot }; + } + } + } + + + await WriteDataAsync(response); + } + } +} diff --git a/EpinelPS/LobbyServer/Msgs/Character/ResetLevel.cs b/EpinelPS/LobbyServer/Msgs/Character/ResetLevel.cs index 030c703..5b1a910 100644 --- a/EpinelPS/LobbyServer/Msgs/Character/ResetLevel.cs +++ b/EpinelPS/LobbyServer/Msgs/Character/ResetLevel.cs @@ -29,6 +29,11 @@ namespace EpinelPS.LobbyServer.Msgs.Character Logger.Warn("Character level is already 1 - cannot reset"); return; } + if (item.Level == 200) + { + Logger.Warn("Character level is 200 - cannot reset"); + return; + } int requiredCredit = 0; int requiredBattleData = 0; diff --git a/EpinelPS/LobbyServer/Msgs/Character/SynchroLevelUp.cs b/EpinelPS/LobbyServer/Msgs/Character/SynchroLevelUp.cs new file mode 100644 index 0000000..11e9997 --- /dev/null +++ b/EpinelPS/LobbyServer/Msgs/Character/SynchroLevelUp.cs @@ -0,0 +1,60 @@ +using EpinelPS.Database; +using EpinelPS.StaticInfo; +using EpinelPS.Utils; +using Swan.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EpinelPS.LobbyServer.Msgs.Character +{ + [PacketPath("/character/SynchroDevice/LevelUp")] + public class SynchroLevelUp : LobbyMsgHandler + { + protected override async Task HandleAsync() + { + var req = await ReadData(); + var user = GetUser(); + + var response = new ResSynchroLevelUp(); + var data = GameData.Instance.GetCharacterLevelUpData(); + + + int requiredCredit = 0; + int requiredBattleData = 0; + int requiredCoreDust = 0; + var levelUpData = data[user.SynchroDeviceLevel + 1]; + requiredCredit += levelUpData.gold; + requiredBattleData += levelUpData.character_exp; + requiredCoreDust += levelUpData.character_exp2; + + if (user.CanSubtractCurrency(CurrencyType.Gold, requiredCredit) && + user.CanSubtractCurrency(CurrencyType.CharacterExp, requiredBattleData) && + user.CanSubtractCurrency(CurrencyType.CharacterExp2, requiredCoreDust)) + { + user.SubtractCurrency(CurrencyType.Gold, requiredCredit); + user.SubtractCurrency(CurrencyType.CharacterExp, requiredBattleData); + user.SubtractCurrency(CurrencyType.CharacterExp2, requiredCoreDust); + user.SynchroDeviceLevel++; + } + else + { + // TOOD: log this + Logger.Error("ERROR: Not enough currency for upgrade"); + return; + } + + + foreach (var currency in user.Currency) + { + response.Currencies.Add(new NetUserCurrencyData() { Type = (int)currency.Key, Value = currency.Value }); + } + response.SynchroLv = user.SynchroDeviceLevel; + + JsonDb.Save(); + await WriteDataAsync(response); + } + } +} diff --git a/EpinelPS/LobbyServer/Msgs/Character/UnregisterSynchroDevice.cs b/EpinelPS/LobbyServer/Msgs/Character/UnregisterSynchroDevice.cs new file mode 100644 index 0000000..708ed4a --- /dev/null +++ b/EpinelPS/LobbyServer/Msgs/Character/UnregisterSynchroDevice.cs @@ -0,0 +1,70 @@ +using EpinelPS.Database; +using EpinelPS.Utils; +using Swan.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace EpinelPS.LobbyServer.Msgs.Character +{ + [PacketPath("/character/SynchroDevice/Unregist")] + public class UnregisterSynchroDevice : LobbyMsgHandler + { + protected override async Task HandleAsync() + { + var req = await ReadData(); + var user = GetUser(); + + var response = new ResSynchroUnregist(); + + foreach (var item in user.SynchroSlots) + { + if (item.Slot == req.Slot) + { + if (item.CharacterSerialNumber == 0) + { + Logger.Warn("must add character from synchrodevice first"); + } + else + { + var oldCSN = item.CharacterSerialNumber; + item.CharacterSerialNumber = 0; + var data = user.GetCharacterBySerialNumber(oldCSN); + if (data == null) throw new Exception("failed to lookup character"); + + response.Character = new NetUserCharacterDefaultData() + { + Csn = data.Csn, + CostumeId = data.CostumeId, + Grade = data.Grade, + Level = data.Level, + Skill1Lv = data.Skill1Lvl, + Skill2Lv = data.Skill2Lvl, + Tid = data.Tid, + UltiSkillLv = data.UltimateLevel + }; + response.Slot = new NetSynchroSlot() { AvailableRegisterAt = item.AvailableAt, Csn = item.CharacterSerialNumber, Slot = item.Slot }; + + response.IsSynchro = false; + var highestLevelCharacters = user.Characters.OrderByDescending(x => x.Level).Take(5).ToList(); + + + foreach (var item2 in highestLevelCharacters) + { + response.SynchroStandardCharacters.Add(item2.Csn); + } + + response.SynchroLv = user.GetSynchroLevel(); + } + } + } + + JsonDb.Save(); + + + await WriteDataAsync(response); + } + } +} diff --git a/EpinelPS/LobbyServer/Msgs/Stage/ClearStage.cs b/EpinelPS/LobbyServer/Msgs/Stage/ClearStage.cs index e10b833..e74dd42 100644 --- a/EpinelPS/LobbyServer/Msgs/Stage/ClearStage.cs +++ b/EpinelPS/LobbyServer/Msgs/Stage/ClearStage.cs @@ -125,7 +125,7 @@ namespace EpinelPS.LobbyServer.Msgs.Stage var newXp = rewardData.user_exp + user.userPointData.ExperiencePoint; var oldXpData = GameData.Instance.GetUserLevelFromUserExp(user.userPointData.ExperiencePoint); - var newLevelExp = GameData.Instance.GetUserMinXpForLevel(user.userPointData.UserLevel + 1); + var newLevelExp = GameData.Instance.GetUserMinXpForLevel(user.userPointData.UserLevel); var newLevel = user.userPointData.UserLevel; if (newLevelExp == -1) @@ -143,7 +143,7 @@ namespace EpinelPS.LobbyServer.Msgs.Stage else user.Currency.Add(CurrencyType.FreeCash, 30); - newLevelExp = GameData.Instance.GetUserMinXpForLevel(newLevel + 1); + newLevelExp = GameData.Instance.GetUserMinXpForLevel(newLevel); } diff --git a/EpinelPS/LobbyServer/Msgs/User/EnterLobbyServer.cs b/EpinelPS/LobbyServer/Msgs/User/EnterLobbyServer.cs index 89b709d..44528d8 100644 --- a/EpinelPS/LobbyServer/Msgs/User/EnterLobbyServer.cs +++ b/EpinelPS/LobbyServer/Msgs/User/EnterLobbyServer.cs @@ -46,9 +46,9 @@ namespace EpinelPS.LobbyServer.Msgs.User foreach (var item in user.Characters) { - response.Character.Add(new NetUserCharacterData() { Default = new() { Csn = item.Csn, Skill1Lv = item.Skill1Lvl, Skill2Lv = item.Skill2Lvl, CostumeId = item.CostumeId, Level = item.Level, Grade = item.Grade, Tid = item.Tid, UltiSkillLv = item.UltimateLevel } }); + response.Character.Add(new NetUserCharacterData() { Default = new() { Csn = item.Csn, Skill1Lv = item.Skill1Lvl, Skill2Lv = item.Skill2Lvl, CostumeId = item.CostumeId, Level = user.GetCharacterLevel(item.Csn, item.Level), Grade = item.Grade, Tid = item.Tid, UltiSkillLv = item.UltimateLevel}, IsSynchro = user.GetSynchro(item.Csn) }); } - + foreach (var item in NetUtils.GetUserItems(user)) { response.Items.Add(item); @@ -58,7 +58,8 @@ namespace EpinelPS.LobbyServer.Msgs.User if (user.Characters.Count > 0) { var highestLevelCharacters = user.Characters.OrderByDescending(x => x.Level).Take(5).ToList(); - response.SynchroLv = highestLevelCharacters.Last().Level; + response.SynchroLv = user.GetSynchroLevel(); + foreach (var item in highestLevelCharacters) { response.SynchroStandardCharacters.Add(item.Csn);