Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Differentiate Random Stats by Random Make Type #302

Merged
merged 13 commits into from
Feb 8, 2025
2 changes: 1 addition & 1 deletion Maple2.File.Ingest/Maple2.File.Ingest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<ItemGroup>
<PackageReference Include="Crc32.NET" Version="1.2.0" />
<PackageReference Include="CsvHelper" Version="32.0.2" />
<PackageReference Include="Maple2.File.Parser.Tadeucci" Version="2.2.0" />
<PackageReference Include="Maple2.File.Parser.Tadeucci" Version="2.2.1" />
<PackageReference Include="DotRecast.Core" Version="2024.2.3" />
<PackageReference Include="DotRecast.Detour" Version="2024.2.3" />
<PackageReference Include="DotRecast.Recast" Version="2024.2.3" />
Expand Down
2 changes: 1 addition & 1 deletion Maple2.File.Ingest/Mapper/ItemMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ protected override IEnumerable<ItemMetadata> Map() {
StaticId: data.option.@static,
StaticType: data.option.staticMakeType,
RandomId: data.option.random,
RandomType: data.option.randomMakeType,
RandomType: (RandomMakeType) data.option.randomMakeType,
ConstantId: data.option.constant,
ConstantType: data.option.constantMakeType,
LevelFactor: levelFactor,
Expand Down
63 changes: 44 additions & 19 deletions Maple2.File.Ingest/Mapper/TableMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -460,10 +460,10 @@ private ItemOptionPickTable ParseItemOptionPick() {
}

private ItemVariationTable ParseItemVariation() {
var values = new Dictionary<BasicAttribute, ItemVariationTable.Range<int>>();
var rates = new Dictionary<BasicAttribute, ItemVariationTable.Range<float>>();
var specialValues = new Dictionary<SpecialAttribute, ItemVariationTable.Range<int>>();
var specialRates = new Dictionary<SpecialAttribute, ItemVariationTable.Range<float>>();
var values = new Dictionary<BasicAttribute, List<ItemVariationTable.Range<int>>>();
var rates = new Dictionary<BasicAttribute, List<ItemVariationTable.Range<float>>>();
var specialValues = new Dictionary<SpecialAttribute, List<ItemVariationTable.Range<int>>>();
var specialRates = new Dictionary<SpecialAttribute, List<ItemVariationTable.Range<float>>>();
foreach (ItemOptionVariation.Option option in optionParser.ParseVariation()) {
string name = option.OptionName;
if (name.StartsWith("sid")) continue; // Don't know what stat this maps to.
Expand All @@ -472,11 +472,19 @@ private ItemVariationTable ParseItemVariation() {
var variation = new ItemVariationTable.Range<int>(
Min: option.OptionValueMin,
Max: option.OptionValueMax,
Interval: option.OptionValueVariation);
Variation: option.OptionValueVariation);
try {
values[name.ToBasicAttribute()] = variation;
if (values.ContainsKey(name.ToBasicAttribute())) {
values[name.ToBasicAttribute()].Add(variation);
} else {
values.Add(name.ToBasicAttribute(), [variation]);
}
} catch (ArgumentOutOfRangeException) {
specialValues[name.ToSpecialAttribute()] = variation;
if (specialValues.ContainsKey(name.ToSpecialAttribute())) {
specialValues[name.ToSpecialAttribute()].Add(variation);
} else {
specialValues.Add(name.ToSpecialAttribute(), [variation]);
}
}
} else if (option.OptionRateVariation != 0) {
if (name.EndsWith("_rate")) {
Expand All @@ -486,30 +494,45 @@ private ItemVariationTable ParseItemVariation() {
var variation = new ItemVariationTable.Range<float>(
Min: option.OptionRateMin,
Max: option.OptionRateMax,
Interval: option.OptionRateVariation);
Variation: option.OptionRateVariation);
try {
rates[name.ToBasicAttribute()] = variation;
if (rates.ContainsKey(name.ToBasicAttribute())) {
rates[name.ToBasicAttribute()].Add(variation);
} else {
rates.Add(name.ToBasicAttribute(), [variation]);
}
} catch (ArgumentOutOfRangeException) {
specialRates[name.ToSpecialAttribute()] = variation;
if (specialRates.ContainsKey(name.ToSpecialAttribute())) {
specialRates[name.ToSpecialAttribute()].Add(variation);
} else {
specialRates.Add(name.ToSpecialAttribute(), [variation]);
}
}
}
}

return new ItemVariationTable(values, rates, specialValues, specialRates);
Dictionary<BasicAttribute, ItemVariationTable.Range<int>[]> valuesArray = values.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToArray());
Dictionary<BasicAttribute, ItemVariationTable.Range<float>[]> ratesArray = rates.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToArray());
Dictionary<SpecialAttribute, ItemVariationTable.Range<int>[]> specialValuesArray = specialValues.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToArray());
Dictionary<SpecialAttribute, ItemVariationTable.Range<float>[]> specialRatesArray = specialRates.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToArray());

return new ItemVariationTable(valuesArray, ratesArray, specialValuesArray, specialRatesArray);
}

private IEnumerable<(string Type, ItemEquipVariationTable Table)> ParseItemEquipVariation() {
foreach ((string type, List<ItemOptionVariationEquip.Option> options) in optionParser.ParseVariationEquip()) {
var values = new Dictionary<BasicAttribute, int[]>();
var rates = new Dictionary<BasicAttribute, float[]>();
var specialValues = new Dictionary<SpecialAttribute, int[]>();
var specialRates = new Dictionary<SpecialAttribute, float[]>();
var values = new Dictionary<BasicAttribute, ItemEquipVariationTable.Set<int>[]>();
var rates = new Dictionary<BasicAttribute, ItemEquipVariationTable.Set<float>[]>();
var specialValues = new Dictionary<SpecialAttribute, ItemEquipVariationTable.Set<int>[]>();
var specialRates = new Dictionary<SpecialAttribute, ItemEquipVariationTable.Set<float>[]>();
foreach (ItemOptionVariationEquip.Option option in options) {
string name = option.name.ToLower();
if (name.EndsWith("value")) {
int[] entries = new int[18];
var entries = new ItemEquipVariationTable.Set<int>[18];
for (int i = 0; i < 18; i++) {
entries[i] = (int) option[i];
entries[i] = new ItemEquipVariationTable.Set<int>(
Value: (int) option[i],
Weight: 1); // TODO: Weight
}

name = name[..^"value".Length]; // Remove suffix
Expand All @@ -520,9 +543,11 @@ private ItemVariationTable ParseItemVariation() {
}

} else if (name.EndsWith("rate")) {
float[] entries = new float[18];
var entries = new ItemEquipVariationTable.Set<float>[18];
for (int i = 0; i < 18; i++) {
entries[i] = option[i];
entries[i] = new ItemEquipVariationTable.Set<float>(
Value: option[i],
Weight: 1); // TODO: Weight
}

name = name[..^"rate".Length]; // Remove suffix
Expand Down
6 changes: 6 additions & 0 deletions Maple2.Model/Enum/ItemOptionMakeType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Maple2.Model.Enum;

public enum RandomMakeType {
Base = 0, // uses itemoptionvariation table
Range = 1, // uses itemoptionvariation_* tables
}
2 changes: 1 addition & 1 deletion Maple2.Model/Metadata/ItemMetadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public record ItemMetadataOption(
int StaticId,
int StaticType,
int RandomId,
int RandomType,
RandomMakeType RandomType,
int ConstantId,
int ConstantType,
int LevelFactor,
Expand Down
22 changes: 12 additions & 10 deletions Maple2.Model/Metadata/Table/ItemOptionTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,19 @@ IReadOnlyDictionary<BasicAttribute, int> RandomRate
}

public record ItemVariationTable(
IReadOnlyDictionary<BasicAttribute, ItemVariationTable.Range<int>> Values,
IReadOnlyDictionary<BasicAttribute, ItemVariationTable.Range<float>> Rates,
IReadOnlyDictionary<SpecialAttribute, ItemVariationTable.Range<int>> SpecialValues,
IReadOnlyDictionary<SpecialAttribute, ItemVariationTable.Range<float>> SpecialRates
IReadOnlyDictionary<BasicAttribute, ItemVariationTable.Range<int>[]> Values,
IReadOnlyDictionary<BasicAttribute, ItemVariationTable.Range<float>[]> Rates,
IReadOnlyDictionary<SpecialAttribute, ItemVariationTable.Range<int>[]> SpecialValues,
IReadOnlyDictionary<SpecialAttribute, ItemVariationTable.Range<float>[]> SpecialRates
) : Table {
public readonly record struct Range<T>(T Min, T Max, T Interval) where T : INumber<T>;
public readonly record struct Range<T>(T Min, T Max, T Variation) where T : INumber<T>;
}

public record ItemEquipVariationTable(
IReadOnlyDictionary<BasicAttribute, int[]> Values,
IReadOnlyDictionary<BasicAttribute, float[]> Rates,
IReadOnlyDictionary<SpecialAttribute, int[]> SpecialValues,
IReadOnlyDictionary<SpecialAttribute, float[]> SpecialRates
) : Table;
IReadOnlyDictionary<BasicAttribute, ItemEquipVariationTable.Set<int>[]> Values,
IReadOnlyDictionary<BasicAttribute, ItemEquipVariationTable.Set<float>[]> Rates,
IReadOnlyDictionary<SpecialAttribute, ItemEquipVariationTable.Set<int>[]> SpecialValues,
IReadOnlyDictionary<SpecialAttribute, ItemEquipVariationTable.Set<float>[]> SpecialRates
) : Table {
public record Set<T>(T Value, int Weight) where T : INumber<T>;
}
59 changes: 35 additions & 24 deletions Maple2.Server.Game/PacketHandlers/ChangeAttributesScrollHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
using Maple2.Server.Game.Packets;
using Maple2.Server.Game.Session;
using Maple2.Server.Game.Util;
using static Maple2.Model.Error.ChangeAttributesScrollError;
using Maple2.Tools.Extensions;

namespace Maple2.Server.Game.PacketHandlers;

Expand Down Expand Up @@ -57,21 +57,22 @@ private void HandleChange(GameSession session, IByteReader packet) {
lock (session.Item) {
Item? scroll = session.Item.Inventory.Get(scrollUid, InventoryType.Misc);
if (scroll == null || scroll.Metadata.Function?.Type != ItemFunction.ItemRemakeScroll) {
session.Send(ChangeAttributesScrollPacket.Error(s_itemremake_scroll_error_invalid_scroll));
session.Send(ChangeAttributesScrollPacket.Error(ChangeAttributesScrollError.s_itemremake_scroll_error_invalid_scroll));
return;
}

Item? item = session.Item.Inventory.Get(itemUid, InventoryType.Gear);
if (item == null) {
session.Send(ChangeAttributesScrollPacket.Error(s_itemremake_scroll_error_invalid_target_data));
session.Send(ChangeAttributesScrollPacket.Error(ChangeAttributesScrollError.s_itemremake_scroll_error_invalid_target_data));
return;
}
if (item.Stats == null) {
session.Send(ChangeAttributesScrollPacket.Error(s_itemremake_scroll_error_invalid_target_stat));
session.Send(ChangeAttributesScrollPacket.Error(ChangeAttributesScrollError.s_itemremake_scroll_error_invalid_target_stat));
return;
}

ChangeAttributesScrollError error = IsCompatibleScroll(item, scroll);
if (error != none) {
if (error != ChangeAttributesScrollError.none) {
session.Send(ChangeAttributesScrollPacket.Error(error));
return;
}
Expand All @@ -81,33 +82,43 @@ private void HandleChange(GameSession session, IByteReader packet) {
ItemStats.Option itemOption = item.Stats[ItemStats.Type.Random];
if (isSpecialAttribute) {
if (!itemOption.Special.ContainsKey((SpecialAttribute) attribute)) {
session.Send(ChangeAttributesScrollPacket.Error(s_itemremake_scroll_error_impossible_property));
session.Send(ChangeAttributesScrollPacket.Error(ChangeAttributesScrollError.s_itemremake_scroll_error_impossible_property));
return;
}
} else {
if (!itemOption.Basic.ContainsKey((BasicAttribute) attribute)) {
session.Send(ChangeAttributesScrollPacket.Error(s_itemremake_scroll_error_impossible_property));
session.Send(ChangeAttributesScrollPacket.Error(ChangeAttributesScrollError.s_itemremake_scroll_error_impossible_property));
return;
}
}

lockItem = ChangeAttributesHandler.GetLockConsumeItem(session, item);
if (lockItem == null) {
session.Send(ChangeAttributesScrollPacket.Error(s_itemremake_error_server_fail_lack_lock_consume_item));
session.Send(ChangeAttributesScrollPacket.Error(ChangeAttributesScrollError.s_itemremake_error_server_fail_lack_lock_consume_item));
return;
}
}

// Clone the item so we can preview changes without modifying existing item.
Item changeItem = item.Clone();
if (changeItem.Stats == null) { // This should be impossible, but check again to make linter happy.
session.Send(ChangeAttributesScrollPacket.Error(s_itemremake_scroll_error_invalid_target_stat));
session.Send(ChangeAttributesScrollPacket.Error(ChangeAttributesScrollError.s_itemremake_scroll_error_invalid_target_stat));
return;
}

if (changeItem.Metadata.Option == null) { // This should be impossible, but check again to make linter happy.
session.Send(ChangeAttributesScrollPacket.Error(ChangeAttributesScrollError.s_itemremake_scroll_error_impossible_item));
return;
}

if (!TableMetadata.ItemOptionRandomTable.Options.TryGetValue(changeItem.Metadata.Option.RandomId, changeItem.Rarity, out ItemOption? itemOptionMetadata)) {
session.Send(ChangeAttributesScrollPacket.Error(ChangeAttributesScrollError.s_itemremake_scroll_error_impossible_item));
return;
}

ItemStats.Option changeOption = changeItem.Stats[ItemStats.Type.Random];
if (!ItemStatsCalc.RandomizeValues(changeItem.Type, ref changeOption)) {
session.Send(ChangeAttributesScrollPacket.Error(s_itemremake_scroll_error_server_fail_remake));
if (!ItemStatsCalc.RandomizeValues(changeItem.Type, itemOptionMetadata, changeItem.Metadata.Option, ref changeOption)) {
session.Send(ChangeAttributesScrollPacket.Error(ChangeAttributesScrollError.s_itemremake_scroll_error_server_fail_remake));
return;
}

Expand All @@ -124,11 +135,11 @@ private void HandleChange(GameSession session, IByteReader packet) {
}

if (!session.Item.Inventory.Consume(scroll.Uid, 1)) {
session.Send(ChangeAttributesScrollPacket.Error(s_itemremake_scroll_error_server_fail_consume_scroll));
session.Send(ChangeAttributesScrollPacket.Error(ChangeAttributesScrollError.s_itemremake_scroll_error_server_fail_consume_scroll));
return;
}
if (lockItem != null && !session.Item.Inventory.Consume(lockItem.Uid, 1)) {
session.Send(ChangeAttributesScrollPacket.Error(s_itemremake_error_server_fail_lack_lock_consume_item));
session.Send(ChangeAttributesScrollPacket.Error(ChangeAttributesScrollError.s_itemremake_error_server_fail_lack_lock_consume_item));
return;
}

Expand All @@ -140,19 +151,19 @@ private void HandleChange(GameSession session, IByteReader packet) {
private static void HandleSelect(GameSession session, IByteReader packet) {
long itemUid = packet.ReadLong();
if (session.ChangeAttributesItem == null || session.ChangeAttributesItem.Uid != itemUid) {
session.Send(ChangeAttributesScrollPacket.Error(s_itemremake_scroll_error_server_fail_apply_before_option));
session.Send(ChangeAttributesScrollPacket.Error(ChangeAttributesScrollError.s_itemremake_scroll_error_server_fail_apply_before_option));
return;
}

lock (session.Item) {
Item? item = session.Item.Inventory.Get(itemUid, InventoryType.Gear);
if (item == null) {
session.Send(ChangeAttributesScrollPacket.Error(s_itemremake_scroll_error_invalid_target_data));
session.Send(ChangeAttributesScrollPacket.Error(ChangeAttributesScrollError.s_itemremake_scroll_error_invalid_target_data));
return;
}

if (item.Stats == null || session.ChangeAttributesItem.Stats == null) {
session.Send(ChangeAttributesScrollPacket.Error(s_itemremake_scroll_error_server_fail_apply_before_option));
session.Send(ChangeAttributesScrollPacket.Error(ChangeAttributesScrollError.s_itemremake_scroll_error_server_fail_apply_before_option));
return;
}

Expand All @@ -164,30 +175,30 @@ private static void HandleSelect(GameSession session, IByteReader packet) {

private ChangeAttributesScrollError IsCompatibleScroll(Item item, Item scroll) {
if (item.Rarity is < Constant.ChangeAttributesMinRarity or > Constant.ChangeAttributesMaxRarity) {
return s_itemremake_scroll_error_impossible_rank;
return ChangeAttributesScrollError.s_itemremake_scroll_error_impossible_rank;
}
if (!item.Type.IsWeapon && !item.Type.IsArmor && !item.Type.IsAccessory) {
return s_itemremake_scroll_error_impossible_slot;
return ChangeAttributesScrollError.s_itemremake_scroll_error_impossible_slot;
}
if (item.Metadata.Limit.Level < Constant.ChangeAttributesMinLevel) {
return s_itemremake_scroll_error_impossible_level;
return ChangeAttributesScrollError.s_itemremake_scroll_error_impossible_level;
}

// Validate scroll conditions
if (!int.TryParse(scroll.Metadata.Function?.Parameters, out int remakeId)) {
return s_itemremake_scroll_error_invalid_scroll_data;
return ChangeAttributesScrollError.s_itemremake_scroll_error_invalid_scroll_data;
}
if (!TableMetadata.ItemRemakeScrollTable.Entries.TryGetValue(remakeId, out ItemRemakeScrollMetadata? metadata)) {
return s_itemremake_scroll_error_invalid_scroll_data;
return ChangeAttributesScrollError.s_itemremake_scroll_error_invalid_scroll_data;
}

if (item.Metadata.Limit.Level < metadata.MinLevel || item.Metadata.Limit.Level > metadata.MaxLevel) {
return s_itemremake_scroll_error_impossible_item;
return ChangeAttributesScrollError.s_itemremake_scroll_error_impossible_item;
}
if (!metadata.Rarities.Contains(item.Rarity) || !metadata.ItemTypes.Contains(item.Type.Type)) {
return s_itemremake_scroll_error_impossible_item;
return ChangeAttributesScrollError.s_itemremake_scroll_error_impossible_item;
}

return none;
return ChangeAttributesScrollError.none;
}
}
Loading
Loading