From 31cfbc4f5e03ad61e284d4f386eca376f81c61b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=82ngelo=20Tadeucci?= Date: Thu, 22 May 2025 20:39:38 -0300 Subject: [PATCH 1/3] =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E8=A7=92=E8=89=B2?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E8=AE=A1=E7=AE=97=E9=80=BB=E8=BE=91=20?= =?UTF-8?q?=E5=9C=A8=20`StatsManager.cs`=20=E6=96=87=E4=BB=B6=E4=B8=AD?= =?UTF-8?q?=EF=BC=8C=E6=B7=BB=E5=8A=A0=E4=BA=86=E5=AF=B9=E8=A3=85=E5=A4=87?= =?UTF-8?q?=E5=BC=BA=E5=8C=96=E5=B1=9E=E6=80=A7=E7=9A=84=E5=A4=84=E7=90=86?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E3=80=82=E6=96=B0=E5=A2=9E=E4=BA=86=E5=AF=B9?= =?UTF-8?q?=20`item.Enchant`=20=E5=92=8C=20`item.Enchant.BasicOptions`=20?= =?UTF-8?q?=E7=9A=84=E6=A3=80=E6=9F=A5=EF=BC=8C=E4=BB=A5=E4=BE=BF=E5=B0=86?= =?UTF-8?q?=E5=BC=BA=E5=8C=96=E5=B1=9E=E6=80=A7=E5=8A=A0=E5=88=B0=E8=A7=92?= =?UTF-8?q?=E8=89=B2=E7=9A=84=E5=B1=9E=E6=80=A7=E5=80=BC=E4=B8=AD=EF=BC=8C?= =?UTF-8?q?=E4=BB=8E=E8=80=8C=E4=BD=BF=E8=A7=92=E8=89=B2=E7=9A=84=20GearSc?= =?UTF-8?q?ore=20=E8=AE=A1=E7=AE=97=E6=9B=B4=E5=8A=A0=E5=87=86=E7=A1=AE?= =?UTF-8?q?=EF=BC=8C=E8=80=83=E8=99=91=E4=BA=86=E8=A3=85=E5=A4=87=E7=9A=84?= =?UTF-8?q?=E5=BC=BA=E5=8C=96=E6=95=88=E6=9E=9C=E3=80=82Fixed=20the=20issu?= =?UTF-8?q?e=20that=20enhancements=20were=20not=20implemented/applied=20to?= =?UTF-8?q?=20character=20attributes.=EF=BC=88#452=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Maple2.File.Ingest/Mapper/TableMapper.cs | 5 +- Maple2.Model/Metadata/Table/ScrollTable.cs | 12 +- Maple2.Server.Game/Manager/StatsManager.cs | 12 +- .../PacketHandlers/ChangeAttributesHandler.cs | 4 +- .../ChangeAttributesScrollHandler.cs | 106 +++++++++++++----- 5 files changed, 103 insertions(+), 36 deletions(-) diff --git a/Maple2.File.Ingest/Mapper/TableMapper.cs b/Maple2.File.Ingest/Mapper/TableMapper.cs index 0768e1f73..e0920307f 100644 --- a/Maple2.File.Ingest/Mapper/TableMapper.cs +++ b/Maple2.File.Ingest/Mapper/TableMapper.cs @@ -889,7 +889,10 @@ private ItemRemakeScrollTable ParseItemRemakeScrollTable() { MinLevel: scroll.minLv, MaxLevel: scroll.maxLv, ItemTypes: scroll.slot, - Rarities: scroll.rank)); + Rarities: scroll.rank, + RollAttribute: scroll.addOpKind == 1, + RollValueType: (RollValueType) scroll.addOpValue, + OnlyPet: scroll.onlyPet)); } return new ItemRemakeScrollTable(results); diff --git a/Maple2.Model/Metadata/Table/ScrollTable.cs b/Maple2.Model/Metadata/Table/ScrollTable.cs index ac3f0a110..2c4b51822 100644 --- a/Maple2.Model/Metadata/Table/ScrollTable.cs +++ b/Maple2.Model/Metadata/Table/ScrollTable.cs @@ -14,7 +14,6 @@ public record EnchantScrollMetadata( int[] ItemTypes, int[] Rarities); - public record ItemExchangeScrollTable(IReadOnlyDictionary Entries) : Table; public record ItemExchangeScrollMetadata( @@ -30,8 +29,16 @@ public record ItemRemakeScrollMetadata( short MinLevel, short MaxLevel, int[] ItemTypes, - int[] Rarities); + int[] Rarities, + bool RollAttribute, + RollValueType RollValueType, + bool OnlyPet); +public enum RollValueType { + None = 0, + Normal = 1, + HigherEnd = 2, +} public record ItemRepackingScrollTable(IReadOnlyDictionary Entries) : Table; @@ -42,7 +49,6 @@ public record ItemRepackingScrollMetadata( int[] Rarities, bool IsPet); - public record ItemSocketScrollTable(IReadOnlyDictionary Entries) : Table; public record ItemSocketScrollMetadata( diff --git a/Maple2.Server.Game/Manager/StatsManager.cs b/Maple2.Server.Game/Manager/StatsManager.cs index 434705ee6..2d6e69ba5 100644 --- a/Maple2.Server.Game/Manager/StatsManager.cs +++ b/Maple2.Server.Game/Manager/StatsManager.cs @@ -1,4 +1,4 @@ -using Maple2.Model; +using Maple2.Model; using Maple2.Model.Enum; using Maple2.Model.Game; using Maple2.Model.Metadata; @@ -157,6 +157,14 @@ private void AddEquips(FieldPlayer player) { if (item.Stats != null) { AddItemStats(item.Stats); } + + // 新增:把强化属性加到角色属性里 + if (item.Enchant != null && item.Enchant.BasicOptions != null) { + foreach (var kvp in item.Enchant.BasicOptions) { + Values[kvp.Key].AddTotal(kvp.Value); + } + } + Log.Logger.Debug("Calculating Gearscore. Item ID: {id} - Gearscore: {gearscore} - Rarity: {rarity}, Enchant Level: {enchantLevel}, Limit Break Level: {limitBreakLevel}", item.Metadata.Id, item.Metadata.Property.GearScore, item.Rarity, item.Enchant?.Enchants ?? 0, item.LimitBreak?.Level ?? 0); Values.GearScore += Lua.CalcItemLevel(item.Metadata.Property.GearScore, item.Rarity, item.Type.Type, item.Enchant?.Enchants ?? 0, item.LimitBreak?.Level ?? 0).Item1; player.Session.Dungeon.UpdateDungeonEnterLimit(); @@ -216,4 +224,4 @@ private void AddItemStats(ItemStats stats) { } } } -} +} \ No newline at end of file diff --git a/Maple2.Server.Game/PacketHandlers/ChangeAttributesHandler.cs b/Maple2.Server.Game/PacketHandlers/ChangeAttributesHandler.cs index 09fc14d75..dcf347571 100644 --- a/Maple2.Server.Game/PacketHandlers/ChangeAttributesHandler.cs +++ b/Maple2.Server.Game/PacketHandlers/ChangeAttributesHandler.cs @@ -61,7 +61,7 @@ private void HandleChange(GameSession session, IByteReader packet) { lock (session.Item) { // Validate item being changed + lock attributes/lock item if necessary. - Item? item = session.Item.Inventory.Get(itemUid, InventoryType.Gear) + Item? item = session.Item.GetGear(itemUid) ?? session.Item.Inventory.Get(itemUid, InventoryType.Pets); if (item == null) { session.Send(ChangeAttributesPacket.Error(s_itemremake_error_server_not_in_inven)); @@ -166,7 +166,7 @@ private static void HandleSelect(GameSession session, IByteReader packet) { } lock (session.Item) { - Item? item = session.Item.Inventory.Get(itemUid, InventoryType.Gear) + Item? item = session.Item.GetGear(itemUid) ?? session.Item.Inventory.Get(itemUid, InventoryType.Pets); if (item == null) { session.Send(ChangeAttributesPacket.Error(s_itemremake_error_server_not_in_inven)); diff --git a/Maple2.Server.Game/PacketHandlers/ChangeAttributesScrollHandler.cs b/Maple2.Server.Game/PacketHandlers/ChangeAttributesScrollHandler.cs index e4d3b2c3f..c6882459a 100644 --- a/Maple2.Server.Game/PacketHandlers/ChangeAttributesScrollHandler.cs +++ b/Maple2.Server.Game/PacketHandlers/ChangeAttributesScrollHandler.cs @@ -1,4 +1,5 @@ -using Maple2.Database.Storage; +using System.Diagnostics.CodeAnalysis; +using Maple2.Database.Storage; using Maple2.Model.Enum; using Maple2.Model.Error; using Maple2.Model.Game; @@ -61,7 +62,7 @@ private void HandleChange(GameSession session, IByteReader packet) { return; } - Item? item = session.Item.Inventory.Get(itemUid, InventoryType.Gear); + Item? item = session.Item.Inventory.Get(itemUid, InventoryType.Gear) ?? session.Item.Inventory.Get(itemUid, InventoryType.Pets); if (item == null) { session.Send(ChangeAttributesScrollPacket.Error(ChangeAttributesScrollError.s_itemremake_scroll_error_invalid_target_data)); return; @@ -71,8 +72,8 @@ private void HandleChange(GameSession session, IByteReader packet) { return; } - ChangeAttributesScrollError error = IsCompatibleScroll(item, scroll); - if (error != ChangeAttributesScrollError.none) { + ChangeAttributesScrollError error = IsCompatibleScroll(item, scroll, out ItemRemakeScrollMetadata? itemRemakeScrollMetadata); + if (error is not ChangeAttributesScrollError.none || itemRemakeScrollMetadata is null) { session.Send(ChangeAttributesScrollPacket.Error(error)); return; } @@ -116,31 +117,75 @@ private void HandleChange(GameSession session, IByteReader packet) { return; } - ItemStats.Option changeOption = changeItem.Stats[ItemStats.Type.Random]; - if (!ItemStatsCalc.RandomizeValues(changeItem, itemOptionMetadata, ref changeOption)) { - session.Send(ChangeAttributesScrollPacket.Error(ChangeAttributesScrollError.s_itemremake_scroll_error_server_fail_remake)); - return; - } - - // Restore locked attribute values. - if (lockItem != null) { - ItemStats.Option option = item.Stats[ItemStats.Type.Random]; - if (isSpecialAttribute) { - var specialAttribute = (SpecialAttribute) attribute; - changeOption.Special[specialAttribute] = option.Special[specialAttribute]; + if (itemRemakeScrollMetadata.RollAttribute) { + // Randomize attributes. + if (lockItem != null) { + // Add back the locked attribute. + if (isSpecialAttribute) { + if (!ItemStatsCalc.UpdateRandomOption(ref changeItem, new LockOption((SpecialAttribute) attribute, true))) { + session.Send(ChangeAttributesPacket.Error(ChangeAttributesError.s_itemremake_error_server_default)); + return; + } + } else { + if (!ItemStatsCalc.UpdateRandomOption(ref changeItem, new LockOption((BasicAttribute) attribute, true))) { + session.Send(ChangeAttributesPacket.Error(ChangeAttributesError.s_itemremake_error_server_default)); + return; + } + } } else { - var basicAttribute = (BasicAttribute) attribute; - changeOption.Basic[basicAttribute] = option.Basic[basicAttribute]; + if (!ItemStatsCalc.UpdateRandomOption(ref changeItem)) { + session.Send(ChangeAttributesPacket.Error(ChangeAttributesError.s_itemremake_error_server_default)); + return; + } + } + } else { + ItemStats.Option changeOption = changeItem.Stats[ItemStats.Type.Random]; + + if (!ItemStatsCalc.RandomizeValues(changeItem, itemOptionMetadata, ref changeOption)) { + session.Send(ChangeAttributesScrollPacket.Error(ChangeAttributesScrollError.s_itemremake_scroll_error_server_fail_remake)); + return; + } + + // Restore locked attribute values. + if (lockItem != null) { + ItemStats.Option option = item.Stats[ItemStats.Type.Random]; + if (isSpecialAttribute) { + var specialAttribute = (SpecialAttribute) attribute; + changeOption.Special[specialAttribute] = option.Special[specialAttribute]; + } else { + var basicAttribute = (BasicAttribute) attribute; + changeOption.Basic[basicAttribute] = option.Basic[basicAttribute]; + } } } - if (!session.Item.Inventory.Consume(scroll.Uid, 1)) { - session.Send(ChangeAttributesScrollPacket.Error(ChangeAttributesScrollError.s_itemremake_scroll_error_server_fail_consume_scroll)); + // Try to consume both items. + bool consumedScroll = session.Item.Inventory.Consume(scroll.Uid, 1); + bool consumedLock = lockItem == null || session.Item.Inventory.Consume(lockItem.Uid, 1); + + if (!consumedScroll || !consumedLock) { + // Rollback if either consumption failed. + if (consumedScroll) { + Item newScroll = scroll.Clone(); + newScroll.Amount = 1; + session.Item.Inventory.Add(newScroll); // Re-add scroll if only lock failed + } + if (lockItem != null && consumedLock) { + Item newLockItem = lockItem.Clone(); + newLockItem.Amount = 1; + session.Item.Inventory.Add(newLockItem); // Re-add lock item if only scroll failed + } + session.Send(ChangeAttributesScrollPacket.Error( + consumedScroll + ? ChangeAttributesScrollError.s_itemremake_error_server_fail_lack_lock_consume_item + : ChangeAttributesScrollError.s_itemremake_scroll_error_server_fail_consume_scroll + )); return; } - if (lockItem != null && !session.Item.Inventory.Consume(lockItem.Uid, 1)) { - session.Send(ChangeAttributesScrollPacket.Error(ChangeAttributesScrollError.s_itemremake_error_server_fail_lack_lock_consume_item)); - return; + + if (item.Metadata.Limit.TransferType is TransferType.BindPet) { + item.Transfer?.Bind(session.Player.Value.Character); + session.Send(ItemInventoryPacket.UpdateItem(item)); } session.ChangeAttributesItem = changeItem; @@ -156,7 +201,7 @@ private static void HandleSelect(GameSession session, IByteReader packet) { } lock (session.Item) { - Item? item = session.Item.Inventory.Get(itemUid, InventoryType.Gear); + Item? item = session.Item.Inventory.Get(itemUid, InventoryType.Gear) ?? session.Item.Inventory.Get(itemUid, InventoryType.Pets); if (item == null) { session.Send(ChangeAttributesScrollPacket.Error(ChangeAttributesScrollError.s_itemremake_scroll_error_invalid_target_data)); return; @@ -173,11 +218,12 @@ private static void HandleSelect(GameSession session, IByteReader packet) { } } - private ChangeAttributesScrollError IsCompatibleScroll(Item item, Item scroll) { + private ChangeAttributesScrollError IsCompatibleScroll(Item item, Item scroll, out ItemRemakeScrollMetadata? metadata) { + metadata = null; if (item.Rarity is < Constant.ChangeAttributesMinRarity or > Constant.ChangeAttributesMaxRarity) { return ChangeAttributesScrollError.s_itemremake_scroll_error_impossible_rank; } - if (!item.Type.IsWeapon && !item.Type.IsArmor && !item.Type.IsAccessory) { + if (!item.Type.IsWeapon && item.Type is { IsArmor: false, IsAccessory: false, IsPet: false }) { return ChangeAttributesScrollError.s_itemremake_scroll_error_impossible_slot; } if (item.Metadata.Limit.Level < Constant.ChangeAttributesMinLevel) { @@ -188,14 +234,18 @@ private ChangeAttributesScrollError IsCompatibleScroll(Item item, Item scroll) { if (!int.TryParse(scroll.Metadata.Function?.Parameters, out int remakeId)) { return ChangeAttributesScrollError.s_itemremake_scroll_error_invalid_scroll_data; } - if (!TableMetadata.ItemRemakeScrollTable.Entries.TryGetValue(remakeId, out ItemRemakeScrollMetadata? metadata)) { + if (!TableMetadata.ItemRemakeScrollTable.Entries.TryGetValue(remakeId, out metadata)) { return ChangeAttributesScrollError.s_itemremake_scroll_error_invalid_scroll_data; } if (item.Metadata.Limit.Level < metadata.MinLevel || item.Metadata.Limit.Level > metadata.MaxLevel) { return ChangeAttributesScrollError.s_itemremake_scroll_error_impossible_item; } - if (!metadata.Rarities.Contains(item.Rarity) || !metadata.ItemTypes.Contains(item.Type.Type)) { + if (!metadata.Rarities.Contains(item.Rarity) || !metadata.ItemTypes.Contains(item.Type.Type) && !metadata.OnlyPet) { + return ChangeAttributesScrollError.s_itemremake_scroll_error_impossible_item; + } + + if (metadata.OnlyPet && !item.Type.IsPet) { return ChangeAttributesScrollError.s_itemremake_scroll_error_impossible_item; } From fe7f94f0df94fb80170097408157b146d4c4b361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=82ngelo=20Tadeucci?= Date: Sun, 25 May 2025 23:40:59 -0300 Subject: [PATCH 2/3] Simplify & translate --- Maple2.Server.Game/Manager/StatsManager.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Maple2.Server.Game/Manager/StatsManager.cs b/Maple2.Server.Game/Manager/StatsManager.cs index 2d6e69ba5..710ad9305 100644 --- a/Maple2.Server.Game/Manager/StatsManager.cs +++ b/Maple2.Server.Game/Manager/StatsManager.cs @@ -1,4 +1,4 @@ -using Maple2.Model; +using Maple2.Model; using Maple2.Model.Enum; using Maple2.Model.Game; using Maple2.Model.Metadata; @@ -158,9 +158,9 @@ private void AddEquips(FieldPlayer player) { AddItemStats(item.Stats); } - // 新增:把强化属性加到角色属性里 - if (item.Enchant != null && item.Enchant.BasicOptions != null) { - foreach (var kvp in item.Enchant.BasicOptions) { + // Add enhancement attributes to character attributes + if (item.Enchant is not null) { + foreach (KeyValuePair kvp in item.Enchant.BasicOptions) { Values[kvp.Key].AddTotal(kvp.Value); } } @@ -214,9 +214,6 @@ private void StatConversion(FieldPlayer player) { private void AddItemStats(ItemStats stats) { for (int type = 0; type < ItemStats.TYPE_COUNT; type++) { foreach ((BasicAttribute attribute, BasicOption option) in stats[(ItemStats.Type) type].Basic) { - if (attribute is BasicAttribute.CriticalDamage) { - Log.Logger.Information("Adding {attribute} {option} to {actor}", attribute, option, Actor); - } Values[attribute].AddTotal(option); } foreach ((SpecialAttribute attribute, SpecialOption option) in stats[(ItemStats.Type) type].Special) { @@ -224,4 +221,4 @@ private void AddItemStats(ItemStats stats) { } } } -} \ No newline at end of file +} From 0caf26132766b885741b54476aac090c9af92df5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=82ngelo=20Tadeucci?= Date: Mon, 26 May 2025 12:41:58 -0300 Subject: [PATCH 3/3] Update StatsManager.cs --- Maple2.Server.Game/Manager/StatsManager.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Maple2.Server.Game/Manager/StatsManager.cs b/Maple2.Server.Game/Manager/StatsManager.cs index 710ad9305..b8035b882 100644 --- a/Maple2.Server.Game/Manager/StatsManager.cs +++ b/Maple2.Server.Game/Manager/StatsManager.cs @@ -165,6 +165,12 @@ private void AddEquips(FieldPlayer player) { } } + if (item.LimitBreak is not null) { + foreach (KeyValuePair kvp in item.LimitBreak.BasicOptions) { + Values[kvp.Key].AddTotal(kvp.Value); + } + } + Log.Logger.Debug("Calculating Gearscore. Item ID: {id} - Gearscore: {gearscore} - Rarity: {rarity}, Enchant Level: {enchantLevel}, Limit Break Level: {limitBreakLevel}", item.Metadata.Id, item.Metadata.Property.GearScore, item.Rarity, item.Enchant?.Enchants ?? 0, item.LimitBreak?.Level ?? 0); Values.GearScore += Lua.CalcItemLevel(item.Metadata.Property.GearScore, item.Rarity, item.Type.Type, item.Enchant?.Enchants ?? 0, item.LimitBreak?.Level ?? 0).Item1; player.Session.Dungeon.UpdateDungeonEnterLimit();