diff --git a/EpinelPS/Database/JsonDb.cs b/EpinelPS/Database/JsonDb.cs index b721b8a..2bc7dc5 100644 --- a/EpinelPS/Database/JsonDb.cs +++ b/EpinelPS/Database/JsonDb.cs @@ -183,6 +183,7 @@ namespace EpinelPS.Database public NetWallpaperJukeboxFavorite[] WallpaperFavoriteList = []; public NetWallpaperPlaylist[] WallpaperPlaylistList = []; public NetWallpaperJukebox[] WallpaperJukeboxList = []; + public List LobbyDecoBackgroundList = []; public Dictionary UserTeams = new Dictionary(); @@ -208,6 +209,8 @@ namespace EpinelPS.Database public OutpostBuffs OutpostBuffs = new(); public Dictionary ContentsOpenUnlocked = new(); + public List StageClearHistorys = []; + // Event data public Dictionary EventInfo = new(); public MogMinigameInfo MogInfo = new(); @@ -382,24 +385,12 @@ namespace EpinelPS.Database if (item.Isn == isn) { - if (item.Count == 1) + removed++; + item.Count -= count; + + if (item.Count < 0) { - Items.Remove(item); - count--; - } - else - { - // TODO test this - if (item.Count >= count) - { - removed++; - item.Count -= count; - } - else - { - removed += item.Count; - Items.Remove(item); - } + item.Count = 0; } } } diff --git a/EpinelPS/GameData/GameData.cs b/EpinelPS/GameData/GameData.cs index 57bdcfb..7461920 100644 --- a/EpinelPS/GameData/GameData.cs +++ b/EpinelPS/GameData/GameData.cs @@ -32,8 +32,9 @@ namespace EpinelPS.StaticInfo private Dictionary chapterCampaignData; private JArray characterCostumeTable; public Dictionary characterTable; - private Dictionary tutorialTable; - private Dictionary itemEquipTable; + public Dictionary tutorialTable; + public Dictionary itemEquipTable; + public Dictionary itemMaterialTable; private Dictionary FieldMapData = new Dictionary(); // Fixed initialization private Dictionary LevelData = new Dictionary(); // Fixed initialization private Dictionary TacticAcademyLessons = new Dictionary(); // Fixed initialization @@ -54,6 +55,9 @@ namespace EpinelPS.StaticInfo public Dictionary archiveEventDungeonStageRecords = new Dictionary(); public Dictionary userTitleRecords = new Dictionary(); public Dictionary archiveMessengerConditionRecords; + public Dictionary characterStatTable; + public Dictionary skillInfoTable; + public Dictionary costTable; @@ -90,6 +94,10 @@ namespace EpinelPS.StaticInfo ZipStream = new(); tutorialTable = new(); itemEquipTable = new(); + itemMaterialTable = new(); + characterStatTable = new(); + skillInfoTable = new(); + costTable = new(); // Initialize Jukebox data dictionaries jukeboxListDataRecords = new Dictionary(); @@ -320,6 +328,12 @@ namespace EpinelPS.StaticInfo this.itemEquipTable.Add(obj.id, obj); } + var itemMaterialTable = await LoadZip("ItemMaterialTable.json", progress); + foreach (var obj in itemMaterialTable.records) + { + this.itemMaterialTable.Add(obj.id, obj); + } + var characterLevelTable = await LoadZip("CharacterLevelTable.json", progress); foreach (JToken item in characterLevelTable) @@ -460,6 +474,24 @@ namespace EpinelPS.StaticInfo // Load Jukebox data await LoadJukeboxListData(progress); await LoadJukeboxThemeData(progress); + + var characterStatTable = await LoadZip("CharacterStatTable.json", progress); + foreach (var obj in characterStatTable.records) + { + this.characterStatTable.Add(obj.id, obj); + } + + var skillinfoTable = await LoadZip("SkillInfoTable.json", progress); + foreach (var obj in skillinfoTable.records) + { + this.skillInfoTable.Add(obj.id, obj); + } + + var costTable = await LoadZip("CostTable.json", progress); + foreach (var obj in costTable.records) + { + this.costTable.Add(obj.id, obj); + } } public async Task LoadJukeboxListData(ProgressBar bar) diff --git a/EpinelPS/GameData/JsonStaticData.cs b/EpinelPS/GameData/JsonStaticData.cs index 2c3d93a..b8e4cf2 100644 --- a/EpinelPS/GameData/JsonStaticData.cs +++ b/EpinelPS/GameData/JsonStaticData.cs @@ -131,11 +131,38 @@ { public int id; public int piece_id; - public string original_rare; - public string corporation; - public int grade_core_id; - public int name_code; - public int grow_grade; + public string original_rare; + public string corporation; + public int grade_core_id; + public int name_code; + public int grow_grade; + public int stat_enhance_id; + public string character_class; + public List element_id; + public int critical_ratio; + public int critical_damage; + public int shot_id; + public int bonusrange_min; + public int bonusrange_max; + public string use_burst_skill; + public string change_burst_step; + public int burst_apply_delay; + public int burst_duration; + public int ulti_skill_id; + public int skill1_id; + public string skill1_table; + public int skill2_id; + public string skill2_table; + public string eff_category_type; + public int eff_category_value; + public string category_type_1; + public string category_type_2; + public string category_type_3; + public string cv_localkey; + public string squad; + public bool is_visible; + public bool prism_is_active; + public bool is_detail_close; } public class CharacterTable { @@ -427,4 +454,82 @@ public List records; } + public class CharacterStatRecord + { + public int id; + public int group; + public int level; + public int level_hp; + public int level_attack; + public int level_defence; + public int level_energy_resist; + public int level_bio_resist; + } + + public class CharacterStatTable + { + public List records; + } + + public class ItemMaterialRecord + { + public int id; + public string name_localkey; + public string description_localkey; + public string resource_id; + public string item_type; + public string item_sub_type; + public string item_rare; + public int item_value; + public string material_type; + public int material_value; + public int stack_max; + } + + public class ItemMaterialTable + { + public List records; + } + + public class SkillInfoRecord + { + public int id; + public int group_id; + public int skill_level; + public int next_level_id; + public int level_up_cost_id; + public string icon; + public string name_localkey; + public string description_localkey; + public string info_description_localkey; + public List description_value_list; + } + + public class DescriptionValue + { + public string description_value; + } + + public class SkillInfoTable + { + public List records; + } + + public class CostRecord + { + public int id; + public List costs; + } + + public class CostData + { + public string item_type; + public int item_id; + public int item_value; + } + + public class CostTable + { + public List records; + } } diff --git a/EpinelPS/LobbyServer/Msgs/Character/DoLimitBreak.cs b/EpinelPS/LobbyServer/Msgs/Character/DoLimitBreak.cs index 2b564d5..be0f46e 100644 --- a/EpinelPS/LobbyServer/Msgs/Character/DoLimitBreak.cs +++ b/EpinelPS/LobbyServer/Msgs/Character/DoLimitBreak.cs @@ -55,12 +55,9 @@ namespace EpinelPS.LobbyServer.Msgs.Character }; // remove spare body item + var bodyItem = user.Items.FirstOrDefault(i => i.Isn == req.Isn); user.RemoveItemBySerialNumber(req.Isn, 1); - - foreach (var item in user.Items) - { - response.Items.Add(NetUtils.ToNet(item)); - } + response.Items.Add(NetUtils.ToNet(bodyItem)); // replace any reference to the old character to the new TID // Check if RepresentationTeamData exists and has slots diff --git a/EpinelPS/LobbyServer/Msgs/Character/SkillLevelUp.cs b/EpinelPS/LobbyServer/Msgs/Character/SkillLevelUp.cs new file mode 100644 index 0000000..a3867ed --- /dev/null +++ b/EpinelPS/LobbyServer/Msgs/Character/SkillLevelUp.cs @@ -0,0 +1,87 @@ +using EpinelPS.Database; +using EpinelPS.StaticInfo; +using EpinelPS.Utils; + +namespace EpinelPS.LobbyServer.Msgs.Character +{ + [PacketPath("/character/skill/levelup")] + public class SkillLevelUp : LobbyMsgHandler + { + protected override async Task HandleAsync() + { + var req = await ReadData(); + var user = GetUser(); + var response = new ResCharacterSkillLevelUp(); + var character = user.Characters.FirstOrDefault(c => c.Csn == req.Csn); + var charRecord = GameData.Instance.characterTable.Values.FirstOrDefault(c => c.id == character.Tid); + var skillIdMap = new Dictionary + { + { 1, charRecord.ulti_skill_id }, + { 2, charRecord.skill1_id }, + { 3, charRecord.skill2_id } + }; + var skillLevelMap = new Dictionary + { + { 1, character.UltimateLevel }, + { 2, character.Skill1Lvl }, + { 3, character.Skill2Lvl } + }; + var skillRecord = GameData.Instance.skillInfoTable.Values.FirstOrDefault(s => s.id == skillIdMap[req.Category] + (skillLevelMap[req.Category] - 1)); + var costRecord = GameData.Instance.costTable.Values.FirstOrDefault(c => c.id == skillRecord.level_up_cost_id); + + foreach (var cost in costRecord.costs.Where(i => i.item_type != "None")) + { + var item = user.Items.FirstOrDefault(i => i.ItemType == cost.item_id); + + item.Count -= cost.item_value; + + response.Items.Add(new NetUserItemData + { + Isn = item.Isn, + Tid = cost.item_id, + Count = item.Count, + Csn = item.Csn, + Corporation = item.Corp, + Level = item.Level, + Exp = item.Exp, + Position = item.Position + }); + } + + var newChar = new NetUserCharacterDefaultData + { + CostumeId = character.CostumeId, + Csn = character.Csn, + Level = character.Level, + Grade = character.Grade, + Tid = character.Tid, + DispatchTid = character.Tid, + Skill1Lv = character.Skill1Lvl, + Skill2Lv = character.Skill2Lvl, + UltiSkillLv = character.UltimateLevel, + }; + + if (req.Category == 1) + { + character.UltimateLevel++; + newChar.UltiSkillLv++; + } + else if (req.Category == 2) + { + character.Skill1Lvl++; + newChar.Skill1Lv++; + } + else if (req.Category == 3) + { + character.Skill2Lvl++; + newChar.Skill2Lv++; + } + + response.Character = newChar; + + JsonDb.Save(); + + await WriteDataAsync(response); + } + } +} diff --git a/EpinelPS/LobbyServer/Msgs/Character/UpgradeCore.cs b/EpinelPS/LobbyServer/Msgs/Character/UpgradeCore.cs index a167cd8..7b32c36 100644 --- a/EpinelPS/LobbyServer/Msgs/Character/UpgradeCore.cs +++ b/EpinelPS/LobbyServer/Msgs/Character/UpgradeCore.cs @@ -56,12 +56,9 @@ namespace EpinelPS.LobbyServer.Msgs.Character }; // remove spare body item + var bodyItem = user.Items.FirstOrDefault(i => i.Isn == req.Isn); user.RemoveItemBySerialNumber(req.Isn, 1); - - foreach (var item in user.Items) - { - response.Items.Add(NetUtils.ToNet(item)); - } + response.Items.Add(NetUtils.ToNet(bodyItem)); // replace any reference to the old character to the new TID // Check if RepresentationTeamData exists and has slots diff --git a/EpinelPS/LobbyServer/Msgs/Gacha/ExecGacha.cs b/EpinelPS/LobbyServer/Msgs/Gacha/ExecGacha.cs index e9cbbde..c969303 100644 --- a/EpinelPS/LobbyServer/Msgs/Gacha/ExecGacha.cs +++ b/EpinelPS/LobbyServer/Msgs/Gacha/ExecGacha.cs @@ -64,55 +64,58 @@ namespace EpinelPS.LobbyServer.Msgs.Gacha // Add each character's item to user.Items if the character exists in user.Characters foreach (var characterData in selectedCharacters) { - // Check if the item for this character already exists in user.Items based on ItemType - var existingItem = user.Items.FirstOrDefault(item => item.ItemType == characterData.piece_id); + if (user.HasCharacter(characterData.id)) + { + // Check if the item for this character already exists in user.Items based on ItemType + var existingItem = user.Items.FirstOrDefault(item => item.ItemType == characterData.piece_id); - if (existingItem != null) - { - // If the item exists, increment the count - existingItem.Count += 1; + if (existingItem != null) + { + // If the item exists, increment the count + existingItem.Count += 1; - // Send the updated item in the response - response.Items.Add(new NetUserItemData() - { - Tid = existingItem.ItemType, - Csn = existingItem.Csn, - Count = existingItem.Count, - Level = existingItem.Level, - Exp = existingItem.Exp, - Position = existingItem.Position, - Isn = existingItem.Isn - }); - } - else - { - // If the item does not exist, create a new item entry - var newItem = new ItemData() - { - ItemType = characterData.piece_id, - Csn = 0, - Count = 1, // or any relevant count - Level = 0, - Exp = 0, - Position = 0, - Corp = 0, - Isn = user.GenerateUniqueItemId() - }; - user.Items.Add(newItem); + // Send the updated item in the response + response.Items.Add(new NetUserItemData() + { + Tid = existingItem.ItemType, + Csn = existingItem.Csn, + Count = existingItem.Count, + Level = existingItem.Level, + Exp = existingItem.Exp, + Position = existingItem.Position, + Isn = existingItem.Isn + }); + } + else + { + // If the item does not exist, create a new item entry + var newItem = new ItemData() + { + ItemType = characterData.piece_id, + Csn = 0, + Count = 1, // or any relevant count + Level = 0, + Exp = 0, + Position = 0, + Corp = 0, + Isn = user.GenerateUniqueItemId() + }; + user.Items.Add(newItem); - // Add the new item to response - response.Items.Add(new NetUserItemData() - { - Tid = newItem.ItemType, - Csn = newItem.Csn, - Count = newItem.Count, - Level = newItem.Level, - Exp = newItem.Exp, - Position = newItem.Position, - Isn = newItem.Isn - }); - } - } + // Add the new item to response + response.Items.Add(new NetUserItemData() + { + Tid = newItem.ItemType, + Csn = newItem.Csn, + Count = newItem.Count, + Level = newItem.Level, + Exp = newItem.Exp, + Position = newItem.Position, + Isn = newItem.Isn + }); + } + } + } // Populate the 2D array with characterId and pieceId for each selected character foreach (var characterData in selectedCharacters) diff --git a/EpinelPS/LobbyServer/Msgs/Stage/ClearStage.cs b/EpinelPS/LobbyServer/Msgs/Stage/ClearStage.cs index 3b05ca0..e48c729 100644 --- a/EpinelPS/LobbyServer/Msgs/Stage/ClearStage.cs +++ b/EpinelPS/LobbyServer/Msgs/Stage/ClearStage.cs @@ -104,6 +104,8 @@ namespace EpinelPS.LobbyServer.Msgs.Stage } } + // CreateClearInfo(user); + var key = (clearedStage.chapter_id - 1) + "_" + clearedStage.chapter_mod; if (!user.FieldInfoNew.ContainsKey(key)) user.FieldInfoNew.Add(key, new FieldInfoNew()); @@ -309,5 +311,29 @@ namespace EpinelPS.LobbyServer.Msgs.Stage } // TODO: add neon } + + private static void CreateClearInfo(Database.User user) + { + NetStageClearInfo clearInfo = new NetStageClearInfo + { + User = LobbyHandler.CreateWholeUserDataFromDbUser(user), + TeamCombat = user.RepresentationTeamData.TeamCombat, + ClearedAt = DateTimeOffset.UtcNow.Ticks + }; + + foreach (var character in user.RepresentationTeamData.Slots) + { + clearInfo.Slots.Add(new NetStageClearInfoTeam() + { + Slot = character.Slot, + Tid = character.Tid, + Level = character.Level, + Combat = FormulaUtils.CalculateCP(user, character.Csn), + CharacterType = StageClearInfoTeamCharacterType.StageClearInfoTeamCharacterTypeOwnedCharacter // TODO: how do we get this? + }); + } + + user.StageClearHistorys.Add(clearInfo); + } } } diff --git a/EpinelPS/LobbyServer/Msgs/Stage/GetStageClearInfo.cs b/EpinelPS/LobbyServer/Msgs/Stage/GetStageClearInfo.cs new file mode 100644 index 0000000..3af581a --- /dev/null +++ b/EpinelPS/LobbyServer/Msgs/Stage/GetStageClearInfo.cs @@ -0,0 +1,19 @@ +using EpinelPS.Utils; + +namespace EpinelPS.LobbyServer.Msgs.Stage +{ + [PacketPath("/stageclearinfo/get")] + public class GetStageClearInfo : LobbyMsgHandler + { + protected override async Task HandleAsync() + { + var req = await ReadData(); + var response = new ResGetStageClearInfo(); + var user = GetUser(); + + response.Historys.AddRange(user.StageClearHistorys); + + await WriteDataAsync(response); + } + } +} diff --git a/EpinelPS/LobbyServer/Msgs/User/BuyWallpaper.cs b/EpinelPS/LobbyServer/Msgs/User/BuyWallpaper.cs new file mode 100644 index 0000000..caf798c --- /dev/null +++ b/EpinelPS/LobbyServer/Msgs/User/BuyWallpaper.cs @@ -0,0 +1,24 @@ +using EpinelPS.Database; +using EpinelPS.Utils; + +namespace EpinelPS.LobbyServer.Msgs.User +{ + [PacketPath("/user/wallpaper/buy")] + public class BuyWallpaper : LobbyMsgHandler + { + protected override async Task HandleAsync() + { + var req = await ReadData(); + var response = new ResBuyLobbyDecoBackground(); + var user = GetUser(); + + user.LobbyDecoBackgroundList.Add(req.LobbyDecoBackgroundId); + + response.OwnedLobbyDecoBackgroundIdList.Add(user.LobbyDecoBackgroundList); + + JsonDb.Save(); + + await WriteDataAsync(response); + } + } +} diff --git a/EpinelPS/LobbyServer/Msgs/User/EnterLobbyServer.cs b/EpinelPS/LobbyServer/Msgs/User/EnterLobbyServer.cs index f198494..974f979 100644 --- a/EpinelPS/LobbyServer/Msgs/User/EnterLobbyServer.cs +++ b/EpinelPS/LobbyServer/Msgs/User/EnterLobbyServer.cs @@ -85,6 +85,8 @@ namespace EpinelPS.LobbyServer.Msgs.User response.LastClearedNormalMainStageId = user.LastNormalStageCleared; response.TimeRewardBuffs.AddRange(NetUtils.GetOutpostTimeReward(user)); + response.OwnedLobbyDecoBackgroundIdList.AddRange(user.LobbyDecoBackgroundList); + await WriteDataAsync(response); } } diff --git a/EpinelPS/LobbyServer/Msgs/User/GetWallpaper.cs b/EpinelPS/LobbyServer/Msgs/User/GetWallpaper.cs index 6f7ecdd..82ceb09 100644 --- a/EpinelPS/LobbyServer/Msgs/User/GetWallpaper.cs +++ b/EpinelPS/LobbyServer/Msgs/User/GetWallpaper.cs @@ -17,6 +17,7 @@ namespace EpinelPS.LobbyServer.Msgs.User response.WallpaperJukeboxList.AddRange(user.WallpaperJukeboxList); response.WallpaperBackgroundList.AddRange(user.WallpaperBackground); response.WallpaperFavoriteList.AddRange(user.WallpaperFavoriteList); + response.OwnedLobbyDecoBackgroundIdList.AddRange(user.LobbyDecoBackgroundList); // TODO: JukeboxIdList diff --git a/EpinelPS/Program.cs b/EpinelPS/Program.cs index 063c73d..c37016a 100644 --- a/EpinelPS/Program.cs +++ b/EpinelPS/Program.cs @@ -217,13 +217,16 @@ namespace EpinelPS Console.WriteLine(" unban - unban selected user from game"); Console.WriteLine(" exit - exit server application"); Console.WriteLine(" completestage (chapter num)-(stage number) - complete selected stage and get rewards (and all previous ones). Example completestage 15-1. Note that the exact stage number cleared may not be exact."); - Console.WriteLine(" sickpulls (requires selecting user first) allows for all characters to have equal chances of getting pulled"); + Console.WriteLine(" sickpulls (requires selecting user first) allows for all characters to have equal chances of getting pulled"); Console.WriteLine(" SetLevel (level) - Set all characters' level (between 1 and 999 takes effect on game and server restart)"); Console.WriteLine(" SetSkillLevel (level) - Set all characters' skill levels between 1 and 10 (takes effect on game and server restart)"); Console.WriteLine(" addallcharacters - Add all missing characters to the selected user with default levels and skills (takes effect on game and server restart)"); + Console.WriteLine(" addallmaterials (amount) - Add all materials to the selected user with default levels and skills (takes effect on game and server restart)"); + Console.WriteLine(" finishalltutorials - finish all tutorials for the selected user (takes effect on game and server restart)"); Console.WriteLine(" SetCoreLevel (core level / 0-3 sets stars) - Set all characters' grades based on the input (from 0 to 11)"); - - } + Console.WriteLine(" AddItem (id) (amount) - Adds an item to the selected user (takes effect on game and server restart)"); + Console.WriteLine(" AddCharacter (id) - Adds a character to the selected user (takes effect on game and server restart)"); + } else if (input == "ls /users") { Console.WriteLine("Id,Username,Nickname"); @@ -263,299 +266,378 @@ namespace EpinelPS Console.WriteLine("Usage: chroot (user id)"); } } - else if (input == "addallcharacters") - { - if (selectedUser == 0) - { - Console.WriteLine("No user selected"); - } - else - { - var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == selectedUser); - if (user == null) - { - Console.WriteLine("Selected user does not exist"); - selectedUser = 0; - prompt = "# "; - } - else - { - // Group characters by name_code and always add those with grade_core_id == 11, 103, and include grade_core_id == 201 - var allCharacters = GameData.Instance.characterTable.Values - .GroupBy(c => c.name_code) // Group by name_code to treat same name_code as one character 3999 = marian - .SelectMany(g => g.Where(c => c.grade_core_id == 1 || c.grade_core_id == 101 || c.grade_core_id == 201 || c.name_code == 3999)) // Always add characters with grade_core_id == 11 and 103 - .ToList(); + else if (input == "addallcharacters") + { + if (selectedUser == 0) + { + Console.WriteLine("No user selected"); + } + else + { + var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == selectedUser); + if (user == null) + { + Console.WriteLine("Selected user does not exist"); + selectedUser = 0; + prompt = "# "; + } + else + { + // Group characters by name_code and always add those with grade_core_id == 11, 103, and include grade_core_id == 201 + var allCharacters = GameData.Instance.characterTable.Values + .GroupBy(c => c.name_code) // Group by name_code to treat same name_code as one character 3999 = marian + .SelectMany(g => g.Where(c => c.grade_core_id == 1 || c.grade_core_id == 101 || c.grade_core_id == 201 || c.name_code == 3999)) // Always add characters with grade_core_id == 11 and 103 + .ToList(); - foreach (var character in allCharacters) - { - if (!user.HasCharacter(character.id)) - { - user.Characters.Add(new Database.Character() - { - CostumeId = 0, - Csn = user.GenerateUniqueCharacterId(), - Grade = 0, - Level = 1, - Skill1Lvl = 1, - Skill2Lvl = 1, - Tid = character.id, // Tid is the character ID - UltimateLevel = 1 - }); - } - } + foreach (var character in allCharacters) + { + if (!user.HasCharacter(character.id)) + { + user.Characters.Add(new Database.Character() + { + CostumeId = 0, + Csn = user.GenerateUniqueCharacterId(), + Grade = 0, + Level = 1, + Skill1Lvl = 1, + Skill2Lvl = 1, + Tid = character.id, // Tid is the character ID + UltimateLevel = 1 + }); + } + } - Console.WriteLine("Added all missing characters to user " + user.Username); - JsonDb.Save(); - } - } - } - else if (input.StartsWith("SetCoreLevel")) - { - if (selectedUser == 0) - { - Console.WriteLine("No user selected"); - } - else - { - var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == selectedUser); - if (user == null) - { - Console.WriteLine("Selected user does not exist"); - selectedUser = 0; - prompt = "# "; - } - else if (args.Length == 2 && int.TryParse(args[1], out int inputGrade) && inputGrade >= 0 && inputGrade <= 11) - { - foreach (var character in user.Characters) - { - // Get current character's Tid - int tid = character.Tid; + Console.WriteLine("Added all missing characters to user " + user.Username); + JsonDb.Save(); + } + } + } + else if (input == "addallmaterials") + { + if (selectedUser == 0) + { + Console.WriteLine("No user selected"); + } + else + { + var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == selectedUser); + if (user == null) + { + Console.WriteLine("Selected user does not exist"); + selectedUser = 0; + prompt = "# "; + } + else + { + int amount = 1000000; + if (args.Length >= 2) + { + int.TryParse(args[1], out amount); + } - // Get the character data from the character table - if (!GameData.Instance.characterTable.TryGetValue(tid, out var charData)) - { - Console.WriteLine($"Character data not found for Tid {tid}"); - continue; - } + foreach (var tableItem in GameData.Instance.itemMaterialTable.Values) + { + ItemData? item = user.Items.FirstOrDefault(i => i.ItemType == tableItem.id); - int currentGradeCoreId = charData.grade_core_id; - int nameCode = charData.name_code; - string originalRare = charData.original_rare; + if (item == null) + { + user.Items.Add(new ItemData + { + Isn = user.GenerateUniqueItemId(), + ItemType = tableItem.id, + Level = 1, + Exp = 1, + Count = amount + }); + } + else + { + item.Count += amount; + } + } - // Skip characters with original_rare == "R" - if (originalRare == "R" || nameCode == 3999) - { - continue; - } + Console.WriteLine($"Added {amount} of all materials to user " + user.Username); + JsonDb.Save(); + } + } + } + else if (input == "finishalltutorials") + { + if (selectedUser == 0) + { + Console.WriteLine("No user selected"); + } + else + { + var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == selectedUser); + if (user == null) + { + Console.WriteLine("Selected user does not exist"); + selectedUser = 0; + prompt = "# "; + } + else + { + foreach (var tutorial in GameData.Instance.tutorialTable.Values) + { + if (!user.ClearedTutorialData.ContainsKey(tutorial.id)) + { + user.ClearedTutorialData.Add(tutorial.id, tutorial); + } + } - // Now handle normal SR and SSR characters - int maxGradeCoreId = 0; + Console.WriteLine("Finished all tutorials for user " + user.Username); + JsonDb.Save(); + } + } + } + else if (input.StartsWith("SetCoreLevel")) + { + if (selectedUser == 0) + { + Console.WriteLine("No user selected"); + } + else + { + var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == selectedUser); + if (user == null) + { + Console.WriteLine("Selected user does not exist"); + selectedUser = 0; + prompt = "# "; + } + else if (args.Length == 2 && int.TryParse(args[1], out int inputGrade) && inputGrade >= 0 && inputGrade <= 11) + { + foreach (var character in user.Characters) + { + // Get current character's Tid + int tid = character.Tid; - // If the character is "SSR", it can have a grade_core_id from 1 to 11 - if (originalRare == "SSR") - { - maxGradeCoreId = 11; // SSR characters can go from 1 to 11 + // Get the character data from the character table + if (!GameData.Instance.characterTable.TryGetValue(tid, out var charData)) + { + Console.WriteLine($"Character data not found for Tid {tid}"); + continue; + } - // Calculate the new grade_core_id within the bounds - int newGradeCoreId = Math.Min(inputGrade + 1, maxGradeCoreId); // +1 because inputGrade starts from 0 for SSRs + int currentGradeCoreId = charData.grade_core_id; + int nameCode = charData.name_code; + string originalRare = charData.original_rare; - // Find the character with the same name_code and new grade_core_id - var newCharData = GameData.Instance.characterTable.Values.FirstOrDefault(c => - c.name_code == nameCode && c.grade_core_id == newGradeCoreId); + // Skip characters with original_rare == "R" + if (originalRare == "R" || nameCode == 3999) + { + continue; + } - if (newCharData != null) - { - // Update the character's Tid and Grade - character.Tid = newCharData.id; - character.Grade = inputGrade; - } + // Now handle normal SR and SSR characters + int maxGradeCoreId = 0; - } + // If the character is "SSR", it can have a grade_core_id from 1 to 11 + if (originalRare == "SSR") + { + maxGradeCoreId = 11; // SSR characters can go from 1 to 11 - // If the character is "SR", it can have a grade_core_id from 101 to 103 - else if (originalRare == "SR") - { - maxGradeCoreId = 103; // SR characters can go from 101 to 103 + // Calculate the new grade_core_id within the bounds + int newGradeCoreId = Math.Min(inputGrade + 1, maxGradeCoreId); // +1 because inputGrade starts from 0 for SSRs - // Start from 101 and increment based on inputGrade (inputGrade 0 -> grade_core_id 101) - int newGradeCoreId = Math.Min(101 + inputGrade, maxGradeCoreId); // Starts at 101 + // Find the character with the same name_code and new grade_core_id + var newCharData = GameData.Instance.characterTable.Values.FirstOrDefault(c => + c.name_code == nameCode && c.grade_core_id == newGradeCoreId); - // Find the character with the same name_code and new grade_core_id - var newCharData = GameData.Instance.characterTable.Values.FirstOrDefault(c => - c.name_code == nameCode && c.grade_core_id == newGradeCoreId); + if (newCharData != null) + { + // Update the character's Tid and Grade + character.Tid = newCharData.id; + character.Grade = inputGrade; + } - if (newCharData != null) - { - // Update the character's Tid and Grade - character.Tid = newCharData.id; - character.Grade = inputGrade; - } + } - } + // If the character is "SR", it can have a grade_core_id from 101 to 103 + else if (originalRare == "SR") + { + maxGradeCoreId = 103; // SR characters can go from 101 to 103 - } - Console.WriteLine($"Core level of all characters have been set to {inputGrade}"); - JsonDb.Save(); - } - else - { - Console.WriteLine("Invalid argument. Core level must be between 0 and 11."); - } - } - //code above WILL change tids in user.characters so this will update them in representation team - foreach (var user in JsonDb.Instance.Users) - { - // Check if RepresentationTeamData exists and has slots - if (user.RepresentationTeamData != null && user.RepresentationTeamData.Slots != null) - { - // Iterate through RepresentationTeamData slots - foreach (var slot in user.RepresentationTeamData.Slots) - { - // Find the character in user's character list that matches the slot's Csn - var correspondingCharacter = user.Characters.FirstOrDefault(c => c.Csn == slot.Csn); + // Start from 101 and increment based on inputGrade (inputGrade 0 -> grade_core_id 101) + int newGradeCoreId = Math.Min(101 + inputGrade, maxGradeCoreId); // Starts at 101 - if (correspondingCharacter != null) - { - // Update the Tid value if it differs - if (slot.Tid != correspondingCharacter.Tid) - { - slot.Tid = correspondingCharacter.Tid; - } - } - } - } - } + // Find the character with the same name_code and new grade_core_id + var newCharData = GameData.Instance.characterTable.Values.FirstOrDefault(c => + c.name_code == nameCode && c.grade_core_id == newGradeCoreId); - // Save the updated data - JsonDb.Save(); + if (newCharData != null) + { + // Update the character's Tid and Grade + character.Tid = newCharData.id; + character.Grade = inputGrade; + } - } + } + + } + Console.WriteLine($"Core level of all characters have been set to {inputGrade}"); + JsonDb.Save(); + } + else + { + Console.WriteLine("Invalid argument. Core level must be between 0 and 11."); + } + } + //code above WILL change tids in user.characters so this will update them in representation team + foreach (var user in JsonDb.Instance.Users) + { + // Check if RepresentationTeamData exists and has slots + if (user.RepresentationTeamData != null && user.RepresentationTeamData.Slots != null) + { + // Iterate through RepresentationTeamData slots + foreach (var slot in user.RepresentationTeamData.Slots) + { + // Find the character in user's character list that matches the slot's Csn + var correspondingCharacter = user.Characters.FirstOrDefault(c => c.Csn == slot.Csn); + + if (correspondingCharacter != null) + { + // Update the Tid value if it differs + if (slot.Tid != correspondingCharacter.Tid) + { + slot.Tid = correspondingCharacter.Tid; + } + } + } + } + } + + // Save the updated data + JsonDb.Save(); + + } - else if (input == "sickpulls") - { - if (selectedUser == 0) - { - Console.WriteLine("No user selected"); - } - else - { - var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == selectedUser); - if (user == null) - { - Console.WriteLine("Selected user does not exist"); - selectedUser = 0; - prompt = "# "; - } - else - { - // Check current value of sickpulls and toggle it - bool currentSickPulls = EpinelPS.Database.JsonDb.IsSickPulls(user); - if (currentSickPulls) - { - user.sickpulls = false; - Console.WriteLine("sickpulls is now set to false for user " + user.Username); - } - else - { - user.sickpulls = true; - Console.WriteLine("sickpulls is now set to true for user " + user.Username); - } + else if (input == "sickpulls") + { + if (selectedUser == 0) + { + Console.WriteLine("No user selected"); + } + else + { + var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == selectedUser); + if (user == null) + { + Console.WriteLine("Selected user does not exist"); + selectedUser = 0; + prompt = "# "; + } + else + { + // Check current value of sickpulls and toggle it + bool currentSickPulls = EpinelPS.Database.JsonDb.IsSickPulls(user); + if (currentSickPulls) + { + user.sickpulls = false; + Console.WriteLine("sickpulls is now set to false for user " + user.Username); + } + else + { + user.sickpulls = true; + Console.WriteLine("sickpulls is now set to true for user " + user.Username); + } - // Save the changes to the database - JsonDb.Save(); - } - } - } - else if (input.StartsWith("SetLevel")) - { - if (selectedUser == 0) - { - Console.WriteLine("No user selected"); - } - else - { - var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == selectedUser); - if (user == null) - { - Console.WriteLine("Selected user does not exist"); - selectedUser = 0; - prompt = "# "; - } - else if (args.Length == 2 && int.TryParse(args[1], out int level) && level >= 1 && level <= 999) - { - foreach (var character in user.Characters) - { - character.Level = level; - } - Console.WriteLine("Set all characters' level to " + level); - JsonDb.Save(); - } - else - { - Console.WriteLine("Invalid argument. Level must be between 1 and 999."); - } - } - //code above WILL change levels in user.characters so this will update them in representation team - foreach (var user in JsonDb.Instance.Users) - { - // Check if RepresentationTeamData exists and has slots - if (user.RepresentationTeamData != null && user.RepresentationTeamData.Slots != null) - { - // Iterate through RepresentationTeamData slots - foreach (var slot in user.RepresentationTeamData.Slots) - { - // Find the character in user's character list that matches the slot's Csn - var correspondingCharacter = user.Characters.FirstOrDefault(c => c.Csn == slot.Csn); + // Save the changes to the database + JsonDb.Save(); + } + } + } + else if (input.StartsWith("SetLevel")) + { + if (selectedUser == 0) + { + Console.WriteLine("No user selected"); + } + else + { + var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == selectedUser); + if (user == null) + { + Console.WriteLine("Selected user does not exist"); + selectedUser = 0; + prompt = "# "; + } + else if (args.Length == 2 && int.TryParse(args[1], out int level) && level >= 1 && level <= 999) + { + foreach (var character in user.Characters) + { + character.Level = level; + } + Console.WriteLine("Set all characters' level to " + level); + JsonDb.Save(); + } + else + { + Console.WriteLine("Invalid argument. Level must be between 1 and 999."); + } + } + //code above WILL change levels in user.characters so this will update them in representation team + foreach (var user in JsonDb.Instance.Users) + { + // Check if RepresentationTeamData exists and has slots + if (user.RepresentationTeamData != null && user.RepresentationTeamData.Slots != null) + { + // Iterate through RepresentationTeamData slots + foreach (var slot in user.RepresentationTeamData.Slots) + { + // Find the character in user's character list that matches the slot's Csn + var correspondingCharacter = user.Characters.FirstOrDefault(c => c.Csn == slot.Csn); - if (correspondingCharacter != null) - { - // Update the Level value if it differs - if (slot.Level != correspondingCharacter.Level) - { - slot.Level = correspondingCharacter.Level; - } - } - } - } - } + if (correspondingCharacter != null) + { + // Update the Level value if it differs + if (slot.Level != correspondingCharacter.Level) + { + slot.Level = correspondingCharacter.Level; + } + } + } + } + } - // Save the updated data - JsonDb.Save(); - } - else if (input.StartsWith("SetSkillLevel")) - { - if (selectedUser == 0) - { - Console.WriteLine("No user selected"); - } - else - { - var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == selectedUser); - if (user == null) - { - Console.WriteLine("Selected user does not exist"); - selectedUser = 0; - prompt = "# "; - } - else if (args.Length == 2 && int.TryParse(args[1], out int skillLevel) && skillLevel >= 1 && skillLevel <= 10) - { - foreach (var character in user.Characters) - { - character.UltimateLevel = skillLevel; - character.Skill1Lvl = skillLevel; - character.Skill2Lvl = skillLevel; - } - Console.WriteLine("Set all characters' skill levels to " + skillLevel); - JsonDb.Save(); - } - else - { - Console.WriteLine("Invalid argument. Skill level must be between 1 and 10."); - } - } - } + // Save the updated data + JsonDb.Save(); + } + else if (input.StartsWith("SetSkillLevel")) + { + if (selectedUser == 0) + { + Console.WriteLine("No user selected"); + } + else + { + var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == selectedUser); + if (user == null) + { + Console.WriteLine("Selected user does not exist"); + selectedUser = 0; + prompt = "# "; + } + else if (args.Length == 2 && int.TryParse(args[1], out int skillLevel) && skillLevel >= 1 && skillLevel <= 10) + { + foreach (var character in user.Characters) + { + character.UltimateLevel = skillLevel; + character.Skill1Lvl = skillLevel; + character.Skill2Lvl = skillLevel; + } + Console.WriteLine("Set all characters' skill levels to " + skillLevel); + JsonDb.Save(); + } + else + { + Console.WriteLine("Invalid argument. Skill level must be between 1 and 10."); + } + } + } else if (input.StartsWith("rmuser")) { @@ -591,108 +673,218 @@ namespace EpinelPS } } } - else if (input.StartsWith("completestage")) - { - if (selectedUser == 0) - { - Console.WriteLine("No user selected"); - } - else - { - var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == selectedUser); - if (user == null) - { - Console.WriteLine("Selected user does not exist"); - selectedUser = 0; - prompt = "# "; - } - else - { - if (args.Length == 2) - { - var input2 = args[1]; - try - { - var chapterParsed = int.TryParse(input2.Split('-')[0], out int chapterNumber); - var stageParsed = int.TryParse(input2.Split('-')[1], out int stageNumber); + else if (input.StartsWith("completestage")) + { + if (selectedUser == 0) + { + Console.WriteLine("No user selected"); + } + else + { + var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == selectedUser); + if (user == null) + { + Console.WriteLine("Selected user does not exist"); + selectedUser = 0; + prompt = "# "; + } + else + { + if (args.Length == 2) + { + var input2 = args[1]; + try + { + var chapterParsed = int.TryParse(input2.Split('-')[0], out int chapterNumber); + var stageParsed = int.TryParse(input2.Split('-')[1], out int stageNumber); - if (chapterParsed && stageParsed) - { - Console.WriteLine($"Chapter number: {chapterNumber}, Stage number: {stageNumber}"); + if (chapterParsed && stageParsed) + { + Console.WriteLine($"Chapter number: {chapterNumber}, Stage number: {stageNumber}"); - // Complete main stages - for (int i = 0; i <= chapterNumber; i++) - { - var stages = GameData.Instance.GetStageIdsForChapter(i, true); - int target = 1; - foreach (var item in stages) - { - if (!user.IsStageCompleted(item, true)) - { - Console.WriteLine("Completing stage " + item); - ClearStage.CompleteStage(user, item, true); - } + // Complete main stages + for (int i = 0; i <= chapterNumber; i++) + { + var stages = GameData.Instance.GetStageIdsForChapter(i, true); + int target = 1; + foreach (var item in stages) + { + if (!user.IsStageCompleted(item, true)) + { + Console.WriteLine("Completing stage " + item); + ClearStage.CompleteStage(user, item, true); + } - if (i == chapterNumber && target == stageNumber) - { - break; - } + if (i == chapterNumber && target == stageNumber) + { + break; + } - target++; - } - } + target++; + } + } - // Process scenario and regular stages - Console.WriteLine($"Processing stages for chapters 0 to {chapterNumber}"); + // Process scenario and regular stages + Console.WriteLine($"Processing stages for chapters 0 to {chapterNumber}"); - for (int chapter = 0; chapter <= chapterNumber; chapter++) - { - Console.WriteLine($"Processing chapter: {chapter}"); + for (int chapter = 0; chapter <= chapterNumber; chapter++) + { + Console.WriteLine($"Processing chapter: {chapter}"); - var stages = GameData.Instance.GetScenarioStageIdsForChapter(chapter) - .Where(stageId => GameData.Instance.IsValidScenarioStage(stageId, chapterNumber, stageNumber)) - .ToList(); + var stages = GameData.Instance.GetScenarioStageIdsForChapter(chapter) + .Where(stageId => GameData.Instance.IsValidScenarioStage(stageId, chapterNumber, stageNumber)) + .ToList(); - Console.WriteLine($"Found {stages.Count} stages for chapter {chapter}"); + Console.WriteLine($"Found {stages.Count} stages for chapter {chapter}"); - foreach (var stage in stages) - { - if (!user.CompletedScenarios.Contains(stage)) - { - user.CompletedScenarios.Add(stage); - Console.WriteLine($"Added stage {stage} to CompletedScenarios"); - } - else - { - Console.WriteLine($"Stage {stage} is already completed"); - } - } - } + foreach (var stage in stages) + { + if (!user.CompletedScenarios.Contains(stage)) + { + user.CompletedScenarios.Add(stage); + Console.WriteLine($"Added stage {stage} to CompletedScenarios"); + } + else + { + Console.WriteLine($"Stage {stage} is already completed"); + } + } + } - // Save changes to user data - JsonDb.Save(); - } - else - { - Console.WriteLine("Chapter and stage number must be valid integers"); - } - } - catch (Exception ex) - { - Console.WriteLine("Exception: " + ex.ToString()); - } - } - else - { - Console.WriteLine("Invalid argument length, must be 1"); - } - } - } - } + // Save changes to user data + JsonDb.Save(); + } + else + { + Console.WriteLine("Chapter and stage number must be valid integers"); + } + } + catch (Exception ex) + { + Console.WriteLine("Exception: " + ex.ToString()); + } + } + else + { + Console.WriteLine("Invalid argument length, must be 1"); + } + } + } + } + else if (input.StartsWith("AddItem")) + { + if (selectedUser == 0) + { + Console.WriteLine("No user selected"); + } + else + { + var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == selectedUser); + if (user == null) + { + Console.WriteLine("Selected user does not exist"); + selectedUser = 0; + prompt = "# "; + } + else + { + if (args.Length == 3) + { + if (int.TryParse(args[1], out int itemId) && int.TryParse(args[2], out int amount)) + { + ItemData? item = user.Items.FirstOrDefault(i => i.ItemType == itemId); + if (item == null) + { + user.Items.Add(new ItemData + { + Isn = user.GenerateUniqueItemId(), + ItemType = itemId, + Level = 1, + Exp = 1, + Count = amount + }); + } + else + { + item.Count += amount; + if (item.Count < 0) + { + item.Count = 0; + } + } + Console.WriteLine($"Added {amount} of item {itemId} to user {user.Username}"); + JsonDb.Save(); + } + else + { + Console.WriteLine("Invalid item ID or amount"); + } + } + else + { + Console.WriteLine("Invalid argument length, must be 2"); + } + } + } + } + else if (input.StartsWith("AddCharacter")) + { + if (selectedUser == 0) + { + Console.WriteLine("No user selected"); + } + else + { + var user = JsonDb.Instance.Users.FirstOrDefault(x => x.ID == selectedUser); + if (user == null) + { + Console.WriteLine("Selected user does not exist"); + selectedUser = 0; + prompt = "# "; + } + else + { + if (args.Length == 2) + { + if (int.TryParse(args[1], out int characterId)) + { + if (!user.HasCharacter(characterId)) + { + user.Characters.Add(new Database.Character() + { + CostumeId = 0, + Csn = user.GenerateUniqueCharacterId(), + Grade = 0, + Level = 1, + Skill1Lvl = 1, + Skill2Lvl = 1, + Tid = characterId, + UltimateLevel = 1 + }); + Console.WriteLine($"Added character {characterId} to user {user.Username}"); + JsonDb.Save(); + } + else + { + Console.WriteLine($"User {user.Username} already has character {characterId}"); + } + } + else + { + Console.WriteLine("Invalid character ID"); + } + } + else + { + Console.WriteLine("Invalid argument length, must be 1"); + } + } + } + } else if (input == "exit") { Environment.Exit(0); diff --git a/EpinelPS/Utils/FormulaUtils.cs b/EpinelPS/Utils/FormulaUtils.cs new file mode 100644 index 0000000..9e4dc98 --- /dev/null +++ b/EpinelPS/Utils/FormulaUtils.cs @@ -0,0 +1,32 @@ +using EpinelPS.StaticInfo; + +namespace EpinelPS.Utils +{ + public class FormulaUtils + { + public static int CalculateCP(Database.User user, long csn) + { + var character = user.Characters.FirstOrDefault(c => c.Csn == csn); + var charRecord = GameData.Instance.characterTable.Values.FirstOrDefault(c => c.id == character.Tid); + var statRecord = GameData.Instance.characterStatTable.Values.FirstOrDefault(s => charRecord.stat_enhance_id == s.group + (character.Level - 1)); + float coreMult = 1f + character.Grade * 0.02f; + float hp = statRecord.level_hp * coreMult; + float atk = statRecord.level_attack * coreMult; + float def = statRecord.level_defence * coreMult; + float critRate = charRecord.critical_ratio; + float critDamage = charRecord.critical_damage; + float skill1Level = character.Skill1Lvl; + float skill2Level = character.Skill2Lvl; + float ultSkillLevel = character.UltimateLevel; + float critResult = 1 + ((critRate / 10000f) * (critDamage / 10000f - 1)); + float effHealthResult = (def * 100) + hp; + float skillResult = (skill1Level * 0.01f) + (skill2Level * 0.01f) + (ultSkillLevel * 0.02f); + float bondResult = 0f; // TODO + float equipResult = 0f; // TOD + float overloadResult = 0; // TODO + float finalResult = (((critResult * atk * 18) + (effHealthResult * 0.7f)) * (1.3f + skillResult) + bondResult + equipResult + overloadResult) / 100f; + + return (int)Math.Round(finalResult); + } + } +} diff --git a/README.md b/README.md index 7f9abbd..3a95de4 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ To skip stages, a basic command line interface is implemented. - [X] Outpost Rewards - [ ] Admin Panel - [ ] Simulation Room - - [ ] Skill level up + - [X] Skill level up - [ ] Outpost jukebox - [ ] Event system - [ ] Download all game assets ahead of time @@ -57,7 +57,6 @@ To skip stages, a basic command line interface is implemented. ## What is not working: - Events - - Skill upgrade - Mission reward, daily/weekly missions - Side quests - Lots of things in the outpost