From b022eb688cd71ba7e2c14ee9540d33eb0eb8461f Mon Sep 17 00:00:00 2001 From: Kyle873 Date: Fri, 20 Dec 2024 14:39:21 -0500 Subject: [PATCH] Skill upgrade, more commands, fixes (#21) * add BuyWallpaper * added partial stage clear info handling, no longer fails get check the creation of the clear info still has imperfections, so I've left it disabled for now. no point in letting it clutter the DB until the info is at least accurate in the UI. * unfinished CP calculation stuff still need to impl other tables (equipment), need a better way to capture outputs and compare them to retail, not really worth the excess work currently since CP calc isn't used anywhere critical right now * AddItem command * AddCharacter command * finishalltutorials command * addallmaterials command also load the material items table * skill upgrade impl * update README * potential fix for bodies not being removed properly on limit breaks and core upgrades the client seems to be smart enough to deal with zero count item entries, so there's no real reason to remove zero count item entries from the DB * check to make sure we have the character before adding a body * use CreateWholeUserDataFromDbUser instead of doing a manual copy --- EpinelPS/Database/JsonDb.cs | 25 +- EpinelPS/GameData/GameData.cs | 36 +- EpinelPS/GameData/JsonStaticData.cs | 115 ++- .../Msgs/Character/DoLimitBreak.cs | 7 +- .../Msgs/Character/SkillLevelUp.cs | 87 ++ .../LobbyServer/Msgs/Character/UpgradeCore.cs | 7 +- EpinelPS/LobbyServer/Msgs/Gacha/ExecGacha.cs | 95 +- EpinelPS/LobbyServer/Msgs/Stage/ClearStage.cs | 26 + .../Msgs/Stage/GetStageClearInfo.cs | 19 + .../LobbyServer/Msgs/User/BuyWallpaper.cs | 24 + .../LobbyServer/Msgs/User/EnterLobbyServer.cs | 2 + .../LobbyServer/Msgs/User/GetWallpaper.cs | 1 + EpinelPS/Program.cs | 908 +++++++++++------- EpinelPS/Utils/FormulaUtils.cs | 32 + README.md | 3 +- 15 files changed, 947 insertions(+), 440 deletions(-) create mode 100644 EpinelPS/LobbyServer/Msgs/Character/SkillLevelUp.cs create mode 100644 EpinelPS/LobbyServer/Msgs/Stage/GetStageClearInfo.cs create mode 100644 EpinelPS/LobbyServer/Msgs/User/BuyWallpaper.cs create mode 100644 EpinelPS/Utils/FormulaUtils.cs 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