diff --git a/AscNet.Common/Database/Character.cs b/AscNet.Common/Database/Character.cs index 35e00c9..05d31f4 100644 --- a/AscNet.Common/Database/Character.cs +++ b/AscNet.Common/Database/Character.cs @@ -5,14 +5,24 @@ using AscNet.Table.share.character; using AscNet.Table.share.character.skill; using AscNet.Common.MsgPack; using AscNet.Common.Util; +using Newtonsoft.Json; namespace AscNet.Common.Database { #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. public class Character { + public static readonly List levelUpTemplates; public static readonly IMongoCollection collection = Common.db.GetCollection("characters"); + static Character() + { + if (File.Exists("Data/CharacterLevelUpTemplate.json")) + levelUpTemplates = JsonConvert.DeserializeObject>(File.ReadAllText("Data/CharacterLevelUpTemplate.json")) ?? new(); + else + levelUpTemplates = new(); + } + private uint NextEquipId => Equips.MaxBy(x => x.Id)?.Id + 1 ?? 1; public static Character FromUid(long uid) @@ -84,7 +94,39 @@ namespace AscNet.Common.Database Characters.Add(characterData); } - public UpgradeCharacterResult UpgradeCharacterSkillGroup(uint skillGroupId, int count) + public NotifyCharacterDataList.NotifyCharacterDataListCharacterData? AddCharacterExp(int characterId, int exp, int maxLvl = 0) + { + var characterData = TableReaderV2.Parse().FirstOrDefault(x => x.Id == characterId); + var character = Characters.FirstOrDefault(x => x.Id == characterId); + + if (character is not null && characterData is not null) + { + levelCheck: + CharacterLevelUpTemplate? levelUpTemplate = levelUpTemplates.FirstOrDefault(x => x.Level == character.Level && x.Type == characterData.Type); + if (levelUpTemplate is not null) + { + if (levelUpTemplate.Exp > exp) + { + character.Exp += (uint)Math.Max(0, exp); + } + else if (maxLvl > 0 && character.Level == maxLvl) + { + character.Exp = (uint)Math.Max(0, levelUpTemplate.Exp); + } + else + { + character.Level++; + exp -= (int)(levelUpTemplate.Exp - character.Exp); + character.Exp = 0; + goto levelCheck; + } + } + } + + return character; + } + + public UpgradeCharacterSkillResult UpgradeCharacterSkillGroup(uint skillGroupId, int count) { List affectedCharacters = new(); int totalCoinCost = 0; @@ -111,7 +153,7 @@ namespace AscNet.Common.Database } } - return new UpgradeCharacterResult() + return new UpgradeCharacterSkillResult() { AffectedCharacters = affectedCharacters, CoinCost = totalCoinCost, @@ -165,10 +207,25 @@ namespace AscNet.Common.Database public List Fashions { get; set; } } - public struct UpgradeCharacterResult + public struct UpgradeCharacterSkillResult { public int CoinCost { get; init; } public int SkillPointCost { get; init; } public List AffectedCharacters { get; init; } } + + public partial class CharacterLevelUpTemplate + { + [JsonProperty("Level")] + public int Level { get; set; } + + [JsonProperty("Exp")] + public int Exp { get; set; } + + [JsonProperty("AllExp")] + public int AllExp { get; set; } + + [JsonProperty("Type")] + public int Type { get; set; } + } } diff --git a/AscNet.Common/TableExtensions.cs b/AscNet.Common/TableExtensions.cs new file mode 100644 index 0000000..364f511 --- /dev/null +++ b/AscNet.Common/TableExtensions.cs @@ -0,0 +1,49 @@ +using AscNet.Table.V2.share.item; + +namespace AscNet.Common +{ + public static class TableExtensions + { + public static int GetCharacterExp(this ItemTable item, int cardType) + { + if (item.ItemType == (int)ItemType.CardExp && item.SubTypeParams.Count >= 3) + { + int upType = item.SubTypeParams[0]; + int exp = item.SubTypeParams[1]; + int upPercentage = item.SubTypeParams[2] / 100 - 100; + int upMultiple = item.SubTypeParams[2] / 10000; + + if (cardType == upType) + return (int)MathF.Round(exp * upMultiple); + + return exp; + } + + return 0; + } + } + + enum ItemType + { + Assert = 1 << 0, + Money = 1 << 1 | 1 << 0, + Material = 1 << 2, + Fragment = 1 << 3, + Gift = 1 << 4, + WeaponFashion = 1 << 5, + CardExp = 1 << 11 | 1 << 2, + EquipExp = 1 << 12 | 1 << 2, + EquipExpNotInBag = 1 << 12 | 1 << 3, + EquipResonanace = 1 << 13 | 1 << 2, + FurnitureItem = 1 << 14 | 1 << 2, + ExchangeMoney = 1 << 16 | 1 << 2, + SpExchangeMoney = 1 << 17 | 1 << 2, + UnShow = 1 << 18 | 1 << 2, + FavorGift = 1 << 19 | 1 << 2, + ActiveMoney = 1 << 20 | 1 << 2, + PlayingMoney = 1 << 21 | 1 << 2, + PlayingItem = 1 << 22 | 1 << 2, + TRPGItem = 1 << 23 | 1 << 2, + PartnerExp = 1 << 25 | 1 << 2 + } +} diff --git a/AscNet.GameServer/Handlers/CharacterModule.cs b/AscNet.GameServer/Handlers/CharacterModule.cs index c879b47..bf87f26 100644 --- a/AscNet.GameServer/Handlers/CharacterModule.cs +++ b/AscNet.GameServer/Handlers/CharacterModule.cs @@ -1,8 +1,30 @@ using AscNet.Common.Database; using AscNet.Common.MsgPack; +using AscNet.Common.Util; +using AscNet.Table.V2.share.item; +using AscNet.Table.V2.share.character; +using MessagePack; +using AscNet.Common; namespace AscNet.GameServer.Handlers { + #region MsgPackScheme +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + [MessagePackObject(true)] + public class CharacterLevelUpRequest + { + public uint TemplateId; + public Dictionary UseItems; + } + + [MessagePackObject(true)] + public class CharacterLevelUpResponse + { + public int Code; + } +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + #endregion + internal class CharacterModule { [RequestPacketHandler("CharacterUpgradeSkillGroupRequest")] @@ -26,5 +48,42 @@ namespace AscNet.GameServer.Handlers session.SendResponse(new CharacterUpgradeSkillGroupResponse(), packet.Id); } + + [RequestPacketHandler("CharacterLevelUpRequest")] + public static void CharacterLevelUpRequestHandler(Session session, Packet.Request packet) + { + CharacterLevelUpRequest request = packet.Deserialize(); + CharacterTable? characterData = TableReaderV2.Parse().FirstOrDefault(x => x.Id == request.TemplateId); + + if (characterData is null || !session.character.Characters.Any(x => x.Id == characterData.Id)) + { + // CharacterManagerGetCharacterTemplateNotFound + session.SendResponse(new CharacterLevelUpResponse() { Code = 20009001 }, packet.Id); + return; + } + + NotifyItemDataList notifyItemData = new(); + int totalExp = 0; + foreach (var item in request.UseItems) + { + ItemTable? itemTable = TableReaderV2.Parse().FirstOrDefault(x => x.Id == item.Key); + if (itemTable is not null) + { + totalExp += itemTable.GetCharacterExp(characterData.Type) * item.Value; + notifyItemData.ItemDataList.Add(session.inventory.Do(item.Key, item.Value * -1)); + } + } + session.SendPush(notifyItemData); + + var characterUp = session.character.AddCharacterExp(characterData.Id, totalExp, (int)session.player.PlayerData.Level); + if (characterUp is not null) + { + NotifyCharacterDataList notifyCharacterData = new(); + notifyCharacterData.CharacterDataList.Add(characterUp); + session.SendPush(notifyCharacterData); + } + + session.SendResponse(new CharacterLevelUpResponse(), packet.Id); + } } } diff --git a/Resources/Data/CharacterLevelUpTemplate.json b/Resources/Data/CharacterLevelUpTemplate.json new file mode 100644 index 0000000..e54a92a --- /dev/null +++ b/Resources/Data/CharacterLevelUpTemplate.json @@ -0,0 +1,962 @@ +[ + { + "Level": 1, + "Exp": 20, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 2, + "Exp": 40, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 3, + "Exp": 60, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 4, + "Exp": 80, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 5, + "Exp": 100, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 6, + "Exp": 120, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 7, + "Exp": 140, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 8, + "Exp": 160, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 9, + "Exp": 180, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 10, + "Exp": 200, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 11, + "Exp": 220, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 12, + "Exp": 240, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 13, + "Exp": 260, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 14, + "Exp": 280, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 15, + "Exp": 300, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 16, + "Exp": 320, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 17, + "Exp": 340, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 18, + "Exp": 360, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 19, + "Exp": 380, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 20, + "Exp": 400, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 21, + "Exp": 440, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 22, + "Exp": 480, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 23, + "Exp": 520, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 24, + "Exp": 560, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 25, + "Exp": 600, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 26, + "Exp": 640, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 27, + "Exp": 680, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 28, + "Exp": 720, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 29, + "Exp": 760, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 30, + "Exp": 800, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 31, + "Exp": 960, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 32, + "Exp": 1120, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 33, + "Exp": 1280, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 34, + "Exp": 1440, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 35, + "Exp": 1600, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 36, + "Exp": 1760, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 37, + "Exp": 1920, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 38, + "Exp": 2080, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 39, + "Exp": 2240, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 40, + "Exp": 2400, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 41, + "Exp": 2640, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 42, + "Exp": 2880, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 43, + "Exp": 3120, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 44, + "Exp": 3360, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 45, + "Exp": 3600, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 46, + "Exp": 3840, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 47, + "Exp": 4080, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 48, + "Exp": 4320, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 49, + "Exp": 4560, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 50, + "Exp": 4800, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 51, + "Exp": 5280, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 52, + "Exp": 5760, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 53, + "Exp": 6240, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 54, + "Exp": 6720, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 55, + "Exp": 7200, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 56, + "Exp": 7680, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 57, + "Exp": 8160, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 58, + "Exp": 8640, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 59, + "Exp": 9120, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 60, + "Exp": 9600, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 61, + "Exp": 10440, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 62, + "Exp": 11280, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 63, + "Exp": 12120, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 64, + "Exp": 12960, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 65, + "Exp": 13800, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 66, + "Exp": 14640, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 67, + "Exp": 15480, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 68, + "Exp": 16320, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 69, + "Exp": 17160, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 70, + "Exp": 18000, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 71, + "Exp": 19200, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 72, + "Exp": 20400, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 73, + "Exp": 21600, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 74, + "Exp": 22800, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 75, + "Exp": 24000, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 76, + "Exp": 25200, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 77, + "Exp": 26400, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 78, + "Exp": 27600, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 79, + "Exp": 28800, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 80, + "Exp": 30000, + "AllExp": 0, + "Type": 1 + }, + { + "Level": 1, + "Exp": 20, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 2, + "Exp": 60, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 3, + "Exp": 100, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 4, + "Exp": 140, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 5, + "Exp": 180, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 6, + "Exp": 240, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 7, + "Exp": 300, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 8, + "Exp": 360, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 9, + "Exp": 420, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 10, + "Exp": 480, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 11, + "Exp": 560, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 12, + "Exp": 640, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 13, + "Exp": 720, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 14, + "Exp": 800, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 15, + "Exp": 880, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 16, + "Exp": 980, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 17, + "Exp": 1080, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 18, + "Exp": 1180, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 19, + "Exp": 1280, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 20, + "Exp": 1380, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 21, + "Exp": 1500, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 22, + "Exp": 1620, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 23, + "Exp": 1740, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 24, + "Exp": 1860, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 25, + "Exp": 1980, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 26, + "Exp": 2220, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 27, + "Exp": 2460, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 28, + "Exp": 2700, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 29, + "Exp": 2940, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 30, + "Exp": 3180, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 31, + "Exp": 3500, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 32, + "Exp": 3820, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 33, + "Exp": 4140, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 34, + "Exp": 4460, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 35, + "Exp": 4820, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 36, + "Exp": 5180, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 37, + "Exp": 5540, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 38, + "Exp": 5900, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 39, + "Exp": 6440, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 40, + "Exp": 6980, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 41, + "Exp": 7520, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 42, + "Exp": 8060, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 43, + "Exp": 8660, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 44, + "Exp": 9260, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 45, + "Exp": 9860, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 46, + "Exp": 10560, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 47, + "Exp": 11260, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 48, + "Exp": 11960, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 49, + "Exp": 12660, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 50, + "Exp": 13360, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 51, + "Exp": 14360, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 52, + "Exp": 15360, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 53, + "Exp": 16360, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 54, + "Exp": 17360, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 55, + "Exp": 18360, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 56, + "Exp": 19560, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 57, + "Exp": 20760, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 58, + "Exp": 21960, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 59, + "Exp": 23160, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 60, + "Exp": 24360, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 61, + "Exp": 25720, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 62, + "Exp": 27080, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 63, + "Exp": 28440, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 64, + "Exp": 29800, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 65, + "Exp": 31160, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 66, + "Exp": 33160, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 67, + "Exp": 35160, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 68, + "Exp": 37160, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 69, + "Exp": 39160, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 70, + "Exp": 41160, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 71, + "Exp": 43400, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 72, + "Exp": 45640, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 73, + "Exp": 47880, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 74, + "Exp": 50120, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 75, + "Exp": 52360, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 76, + "Exp": 54860, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 77, + "Exp": 57360, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 78, + "Exp": 59860, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 79, + "Exp": 62360, + "AllExp": 0, + "Type": 2 + }, + { + "Level": 80, + "Exp": 0, + "AllExp": 0, + "Type": 2 + } +]