diff --git a/.Lib9c.Tests/Action/ActionEvaluationTest.cs b/.Lib9c.Tests/Action/ActionEvaluationTest.cs index 9a811172d9..6bed9f1a1e 100644 --- a/.Lib9c.Tests/Action/ActionEvaluationTest.cs +++ b/.Lib9c.Tests/Action/ActionEvaluationTest.cs @@ -192,7 +192,10 @@ private ActionBase GetAction(Type type) AuthorizedMiners = Dictionary.Empty, Credits = Dictionary.Empty, }, - ItemEnhancement _ => new ItemEnhancement(), + ItemEnhancement _ => new ItemEnhancement + { + materialIds = new List(), + }, MigrationActivatedAccountsState _ => new MigrationActivatedAccountsState(), MigrationAvatarState _ => new MigrationAvatarState { diff --git a/.Lib9c.Tests/Action/ItemEnhancement11Test.cs b/.Lib9c.Tests/Action/ItemEnhancement11Test.cs new file mode 100644 index 0000000000..2affd37714 --- /dev/null +++ b/.Lib9c.Tests/Action/ItemEnhancement11Test.cs @@ -0,0 +1,234 @@ +namespace Lib9c.Tests.Action +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Linq; + using Bencodex.Types; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Libplanet.Types.Assets; + using Nekoyume; + using Nekoyume.Action; + using Nekoyume.Extensions; + using Nekoyume.Helper; + using Nekoyume.Model.Item; + using Nekoyume.Model.Mail; + using Nekoyume.Model.State; + using Xunit; + using static Nekoyume.Action.ItemEnhancement11; + using static SerializeKeys; + + public class ItemEnhancement11Test + { + private readonly TableSheets _tableSheets; + private readonly Address _agentAddress; + private readonly Address _avatarAddress; + private readonly AvatarState _avatarState; + private readonly Currency _currency; + private IAccountStateDelta _initialState; + + public ItemEnhancement11Test() + { + var sheets = TableSheetsImporter.ImportSheets(); + _tableSheets = new TableSheets(sheets); + var privateKey = new PrivateKey(); + _agentAddress = privateKey.PublicKey.ToAddress(); + var agentState = new AgentState(_agentAddress); + + _avatarAddress = _agentAddress.Derive("avatar"); + _avatarState = new AvatarState( + _avatarAddress, + _agentAddress, + 0, + _tableSheets.GetAvatarSheets(), + new GameConfigState(), + default + ); + + agentState.avatarAddresses.Add(0, _avatarAddress); + +#pragma warning disable CS0618 + // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 + _currency = Currency.Legacy("NCG", 2, null); +#pragma warning restore CS0618 + var gold = new GoldCurrencyState(_currency); + var slotAddress = _avatarAddress.Derive(string.Format(CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, 0)); + + var context = new ActionContext(); + _initialState = new MockStateDelta() + .SetState(_agentAddress, agentState.Serialize()) + .SetState(_avatarAddress, _avatarState.Serialize()) + .SetState(slotAddress, new CombinationSlotState(slotAddress, 0).Serialize()) + .SetState(GoldCurrencyState.Address, gold.Serialize()) + .MintAsset(context, GoldCurrencyState.Address, gold.Currency * 100000000000) + .TransferAsset(context, Addresses.GoldCurrency, _agentAddress, gold.Currency * 1000); + + Assert.Equal(gold.Currency * 99999999000, _initialState.GetBalance(Addresses.GoldCurrency, gold.Currency)); + Assert.Equal(gold.Currency * 1000, _initialState.GetBalance(_agentAddress, gold.Currency)); + + foreach (var (key, value) in sheets) + { + _initialState = _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); + } + } + + [Theory] + [InlineData(0, 1000, true, 0, 1, EnhancementResult.Success, 0, 0, false)] + [InlineData(6, 980, true, 0, 7, EnhancementResult.Success, 0, 0, false)] + [InlineData(0, 1000, false, 1, 1, EnhancementResult.GreatSuccess, 0, 0, false)] + [InlineData(6, 980, false, 10, 6, EnhancementResult.Fail, 0, 320, false)] + [InlineData(6, 980, false, 10, 6, EnhancementResult.Fail, 2, 480, false)] + [InlineData(0, 1000, true, 0, 1, EnhancementResult.Success, 0, 0, true)] + [InlineData(6, 980, true, 0, 7, EnhancementResult.Success, 0, 0, true)] + [InlineData(0, 1000, false, 1, 1, EnhancementResult.GreatSuccess, 0, 0, true)] + [InlineData(6, 980, false, 10, 6, EnhancementResult.Fail, 0, 320, true)] + [InlineData(6, 980, false, 10, 6, EnhancementResult.Fail, 2, 480, true)] + public void Execute( + int level, + int expectedGold, + bool backward, + int randomSeed, + int expectedLevel, + EnhancementResult expected, + int monsterCollectLevel, + int expectedCrystal, + bool stake + ) + { + var context = new ActionContext(); + var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Grade == 1); + var equipment = (Equipment)ItemFactory.CreateItemUsable(row, default, 0, level); + var materialId = Guid.NewGuid(); + var material = (Equipment)ItemFactory.CreateItemUsable(row, materialId, 0, level); + + _avatarState.inventory.AddItem(equipment, count: 1); + _avatarState.inventory.AddItem(material, count: 1); + + var result = new CombinationConsumable5.ResultModel() + { + id = default, + gold = 0, + actionPoint = 0, + recipeId = 1, + materials = new Dictionary(), + itemUsable = equipment, + }; + var preItemUsable = new Equipment((Dictionary)equipment.Serialize()); + + for (var i = 0; i < 100; i++) + { + var mail = new CombinationMail(result, i, default, 0); + _avatarState.Update(mail); + } + + _avatarState.worldInformation.ClearStage(1, 1, 1, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); + + var slotAddress = + _avatarAddress.Derive(string.Format(CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, 0)); + + Assert.Equal(level, equipment.level); + + if (backward) + { + _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); + } + else + { + _initialState = _initialState + .SetState(_avatarAddress.Derive(LegacyInventoryKey), _avatarState.inventory.Serialize()) + .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), _avatarState.worldInformation.Serialize()) + .SetState(_avatarAddress.Derive(LegacyQuestListKey), _avatarState.questList.Serialize()) + .SetState(_avatarAddress, _avatarState.SerializeV2()); + } + + if (monsterCollectLevel > 0) + { + var requiredGold = _tableSheets.StakeRegularRewardSheet.OrderedRows + .First(r => r.Level == monsterCollectLevel).RequiredGold; + if (stake) + { + // StakeState; + var stakeStateAddress = StakeState.DeriveAddress(_agentAddress); + var stakeState = new StakeState(stakeStateAddress, 1); + _initialState = _initialState + .SetState(stakeStateAddress, stakeState.SerializeV2()) + .MintAsset(context, stakeStateAddress, requiredGold * _currency); + } + else + { + var mcAddress = MonsterCollectionState.DeriveAddress(_agentAddress, 0); + _initialState = _initialState.SetState( + mcAddress, + new MonsterCollectionState(mcAddress, monsterCollectLevel, 0).Serialize() + ) + .MintAsset(context, mcAddress, requiredGold * _currency); + } + } + + var action = new ItemEnhancement11() + { + itemId = default, + materialId = materialId, + avatarAddress = _avatarAddress, + slotIndex = 0, + }; + + var nextState = action.Execute(new ActionContext() + { + PreviousState = _initialState, + Signer = _agentAddress, + BlockIndex = 1, + Random = new TestRandom(randomSeed), + }); + + var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); + var resultEquipment = (Equipment)slotState.Result.itemUsable; + var nextAvatarState = nextState.GetAvatarState(_avatarAddress); + Assert.Equal(default, resultEquipment.ItemId); + Assert.Equal(expectedLevel, resultEquipment.level); + Assert.Equal(expectedGold * _currency, nextState.GetBalance(_agentAddress, _currency)); + + var arenaSheet = _tableSheets.ArenaSheet; + var arenaData = arenaSheet.GetRoundByBlockIndex(1); + var feeStoreAddress = Addresses.GetBlacksmithFeeAddress(arenaData.ChampionshipId, arenaData.Round); + Assert.Equal( + (1000 - expectedGold) * _currency, + nextState.GetBalance(feeStoreAddress, _currency) + ); + Assert.Equal(30, nextAvatarState.mailBox.Count); + + var costRow = _tableSheets.EnhancementCostSheetV2 + .OrderedList + .First(x => x.Grade == 1 && x.Level == level + 1); + var stateDict = (Dictionary)nextState.GetState(slotAddress); + var slot = new CombinationSlotState(stateDict); + var slotResult = (ResultModel)slot.Result; + Assert.Equal(expected, slotResult.enhancementResult); + + switch (slotResult.enhancementResult) + { + case EnhancementResult.GreatSuccess: + var baseAtk = preItemUsable.StatsMap.BaseATK * (costRow.BaseStatGrowthMax.NormalizeFromTenThousandths() + 1); + var extraAtk = preItemUsable.StatsMap.AdditionalATK * (costRow.ExtraStatGrowthMax.NormalizeFromTenThousandths() + 1); + Assert.Equal((int)(baseAtk + extraAtk), resultEquipment.StatsMap.ATK); + break; + case EnhancementResult.Success: + var baseMinAtk = preItemUsable.StatsMap.BaseATK * (costRow.BaseStatGrowthMin.NormalizeFromTenThousandths() + 1); + var baseMaxAtk = preItemUsable.StatsMap.BaseATK * (costRow.BaseStatGrowthMax.NormalizeFromTenThousandths() + 1); + var extraMinAtk = preItemUsable.StatsMap.AdditionalATK * (costRow.ExtraStatGrowthMin.NormalizeFromTenThousandths() + 1); + var extraMaxAtk = preItemUsable.StatsMap.AdditionalATK * (costRow.ExtraStatGrowthMax.NormalizeFromTenThousandths() + 1); + Assert.InRange(resultEquipment.StatsMap.ATK, baseMinAtk + extraMinAtk, baseMaxAtk + extraMaxAtk + 1); + break; + case EnhancementResult.Fail: + Assert.Equal(preItemUsable.StatsMap.ATK, resultEquipment.StatsMap.ATK); + break; + } + + Assert.Equal(preItemUsable.TradableId, slotResult.preItemUsable.TradableId); + Assert.Equal(preItemUsable.TradableId, resultEquipment.TradableId); + Assert.Equal(costRow.Cost, slotResult.gold); + Assert.Equal(expectedCrystal * CrystalCalculator.CRYSTAL, slotResult.CRYSTAL); + } + } +} diff --git a/.Lib9c.Tests/Action/ItemEnhancement9Test.cs b/.Lib9c.Tests/Action/ItemEnhancement9Test.cs index 33643d5345..0ac80e3154 100644 --- a/.Lib9c.Tests/Action/ItemEnhancement9Test.cs +++ b/.Lib9c.Tests/Action/ItemEnhancement9Test.cs @@ -164,15 +164,15 @@ public void Execute(int level, int expectedGold, bool backward) var slot = new CombinationSlotState(stateDict); var slotResult = (ItemEnhancement9.ResultModel)slot.Result; - switch ((ItemEnhancement.EnhancementResult)slotResult.enhancementResult) + switch ((ItemEnhancement9.EnhancementResult)slotResult.enhancementResult) { - case ItemEnhancement.EnhancementResult.GreatSuccess: + case ItemEnhancement9.EnhancementResult.GreatSuccess: var baseAtk = preItemUsable.StatsMap.BaseATK * (costRow.BaseStatGrowthMax.NormalizeFromTenThousandths() + 1); var extraAtk = preItemUsable.StatsMap.AdditionalATK * (costRow.ExtraStatGrowthMax.NormalizeFromTenThousandths() + 1); Assert.Equal((int)(baseAtk + extraAtk), resultEquipment.StatsMap.ATK); Assert.Equal(preItemUsable.level + 1, resultEquipment.level); break; - case ItemEnhancement.EnhancementResult.Success: + case ItemEnhancement9.EnhancementResult.Success: var baseMinAtk = preItemUsable.StatsMap.BaseATK * (costRow.BaseStatGrowthMin.NormalizeFromTenThousandths() + 1); var baseMaxAtk = preItemUsable.StatsMap.BaseATK * (costRow.BaseStatGrowthMax.NormalizeFromTenThousandths() + 1); var extraMinAtk = preItemUsable.StatsMap.AdditionalATK * (costRow.ExtraStatGrowthMin.NormalizeFromTenThousandths() + 1); @@ -180,7 +180,7 @@ public void Execute(int level, int expectedGold, bool backward) Assert.InRange(resultEquipment.StatsMap.ATK, (int)(baseMinAtk + extraMinAtk), (int)(baseMaxAtk + extraMaxAtk) + 1); Assert.Equal(preItemUsable.level + 1, resultEquipment.level); break; - case ItemEnhancement.EnhancementResult.Fail: + case ItemEnhancement9.EnhancementResult.Fail: Assert.Equal(preItemUsable.StatsMap.ATK, resultEquipment.StatsMap.ATK); Assert.Equal(preItemUsable.level, resultEquipment.level); break; diff --git a/.Lib9c.Tests/Action/ItemEnhancementTest.cs b/.Lib9c.Tests/Action/ItemEnhancementTest.cs index d9e8c8cb37..d6dcc25546 100644 --- a/.Lib9c.Tests/Action/ItemEnhancementTest.cs +++ b/.Lib9c.Tests/Action/ItemEnhancementTest.cs @@ -5,19 +5,18 @@ namespace Lib9c.Tests.Action using System.Globalization; using System.Linq; using Bencodex.Types; - using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; using Nekoyume.Extensions; - using Nekoyume.Helper; using Nekoyume.Model.Item; using Nekoyume.Model.Mail; using Nekoyume.Model.State; + using Nekoyume.TableData; using Xunit; - using static Lib9c.SerializeKeys; + using static SerializeKeys; public class ItemEnhancementTest { @@ -53,7 +52,11 @@ public ItemEnhancementTest() _currency = Currency.Legacy("NCG", 2, null); #pragma warning restore CS0618 var gold = new GoldCurrencyState(_currency); - var slotAddress = _avatarAddress.Derive(string.Format(CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, 0)); + var slotAddress = _avatarAddress.Derive(string.Format( + CultureInfo.InvariantCulture, + CombinationSlotState.DeriveFormat, + 0 + )); var context = new ActionContext(); _initialState = new MockStateDelta() @@ -61,49 +64,97 @@ public ItemEnhancementTest() .SetState(_avatarAddress, _avatarState.Serialize()) .SetState(slotAddress, new CombinationSlotState(slotAddress, 0).Serialize()) .SetState(GoldCurrencyState.Address, gold.Serialize()) - .MintAsset(context, GoldCurrencyState.Address, gold.Currency * 100000000000) - .TransferAsset(context, Addresses.GoldCurrency, _agentAddress, gold.Currency * 1000); + .MintAsset(context, GoldCurrencyState.Address, gold.Currency * 100_000_000_000) + .TransferAsset( + context, + Addresses.GoldCurrency, + _agentAddress, + gold.Currency * 3_000_000 + ); - Assert.Equal(gold.Currency * 99999999000, _initialState.GetBalance(Addresses.GoldCurrency, gold.Currency)); - Assert.Equal(gold.Currency * 1000, _initialState.GetBalance(_agentAddress, gold.Currency)); + Assert.Equal( + gold.Currency * 99_997_000_000, + _initialState.GetBalance(Addresses.GoldCurrency, gold.Currency) + ); + Assert.Equal( + gold.Currency * 3_000_000, + _initialState.GetBalance(_agentAddress, gold.Currency) + ); foreach (var (key, value) in sheets) { - _initialState = _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); + _initialState = + _initialState.SetState(Addresses.TableSheet.Derive(key), value.Serialize()); } } [Theory] - [InlineData(0, 1000, true, 0, 1, ItemEnhancement.EnhancementResult.Success, 0, 0, false)] - [InlineData(6, 980, true, 0, 7, ItemEnhancement.EnhancementResult.Success, 0, 0, false)] - [InlineData(0, 1000, false, 1, 1, ItemEnhancement.EnhancementResult.GreatSuccess, 0, 0, false)] - [InlineData(6, 980, false, 10, 6, ItemEnhancement.EnhancementResult.Fail, 0, 320, false)] - [InlineData(6, 980, false, 10, 6, ItemEnhancement.EnhancementResult.Fail, 2, 480, false)] - [InlineData(0, 1000, true, 0, 1, ItemEnhancement.EnhancementResult.Success, 0, 0, true)] - [InlineData(6, 980, true, 0, 7, ItemEnhancement.EnhancementResult.Success, 0, 0, true)] - [InlineData(0, 1000, false, 1, 1, ItemEnhancement.EnhancementResult.GreatSuccess, 0, 0, true)] - [InlineData(6, 980, false, 10, 6, ItemEnhancement.EnhancementResult.Fail, 0, 320, true)] - [InlineData(6, 980, false, 10, 6, ItemEnhancement.EnhancementResult.Fail, 2, 480, true)] + // from 0 to 1 using one level 0 material + [InlineData(0, 1, 0, 25, 0, 1)] + // from 0 to N using multiple level 0 materials + [InlineData(0, 2, 0, 87, 0, 3)] + [InlineData(0, 4, 20, 837, 0, 15)] + // from K to K with material(s). Check requiredBlock == 0 + [InlineData(10, 10, 0, 0, 0, 1)] + // from K to N using one level X material + [InlineData(5, 6, 40, 1300, 6, 1)] + // from K to N using multiple materials + [InlineData(5, 7, 120, 3800, 4, 6)] + [InlineData(5, 9, 600, 10275, 7, 5)] + // from 20 to 21 (just to reach level 21 exp) + [InlineData(20, 21, 1310720, 7500, 20, 1)] + // from 20 to 21 (over level 21) + [InlineData(20, 21, 1310720, 7500, 20, 2)] + // from 21 to 21 (no level up) + [InlineData(21, 21, 0, 0, 1, 1)] + [InlineData(21, 21, 0, 0, 21, 1)] + // Test: change of exp, change of level, required block, NCG price public void Execute( - int level, - int expectedGold, - bool backward, - int randomSeed, + int startLevel, int expectedLevel, - ItemEnhancement.EnhancementResult expected, - int monsterCollectLevel, - int expectedCrystal, - bool stake - ) + int expectedCost, + int expectedBlockIndex, + int materialLevel, + int materialCount) { - var context = new ActionContext(); - var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Grade == 1); - var equipment = (Equipment)ItemFactory.CreateItemUsable(row, default, 0, level); - var materialId = Guid.NewGuid(); - var material = (Equipment)ItemFactory.CreateItemUsable(row, materialId, 0, level); + var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Grade == 1 && r.Exp > 0); + var equipment = (Equipment)ItemFactory.CreateItemUsable(row, default, 0, startLevel); + if (startLevel == 0) + { + equipment.exp = (long)row.Exp!; + } + else + { + equipment.exp = _tableSheets.EnhancementCostSheetV3.Values.First(r => + r.Grade == equipment.Grade && r.ItemSubType == equipment.ItemSubType && + r.Level == equipment.level).Exp; + } + var startExp = equipment.exp; _avatarState.inventory.AddItem(equipment, count: 1); - _avatarState.inventory.AddItem(material, count: 1); + + var expectedExpIncrement = 0L; + var materialIds = new List(); + for (var i = 0; i < materialCount; i++) + { + var materialId = Guid.NewGuid(); + materialIds.Add(materialId); + var material = + (Equipment)ItemFactory.CreateItemUsable(row, materialId, 0, materialLevel); + if (materialLevel == 0) + { + material.exp = (long)row.Exp!; + } + else + { + material.exp = _tableSheets.EnhancementCostSheetV3.Values.First(r => + r.Grade == material.Grade && r.ItemSubType == material.ItemSubType && + r.Level == material.level).Exp; + } + + expectedExpIncrement += material.exp; + _avatarState.inventory.AddItem(material, count: 1); + } var result = new CombinationConsumable5.ResultModel() { @@ -122,54 +173,42 @@ bool stake _avatarState.Update(mail); } - _avatarState.worldInformation.ClearStage(1, 1, 1, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); + _avatarState.worldInformation.ClearStage( + 1, + 1, + 1, + _tableSheets.WorldSheet, + _tableSheets.WorldUnlockSheet + ); var slotAddress = - _avatarAddress.Derive(string.Format(CultureInfo.InvariantCulture, CombinationSlotState.DeriveFormat, 0)); - - Assert.Equal(level, equipment.level); + _avatarAddress.Derive(string.Format( + CultureInfo.InvariantCulture, + CombinationSlotState.DeriveFormat, + 0 + )); - if (backward) - { - _initialState = _initialState.SetState(_avatarAddress, _avatarState.Serialize()); - } - else - { - _initialState = _initialState - .SetState(_avatarAddress.Derive(LegacyInventoryKey), _avatarState.inventory.Serialize()) - .SetState(_avatarAddress.Derive(LegacyWorldInformationKey), _avatarState.worldInformation.Serialize()) - .SetState(_avatarAddress.Derive(LegacyQuestListKey), _avatarState.questList.Serialize()) - .SetState(_avatarAddress, _avatarState.SerializeV2()); - } + Assert.Equal(startLevel, equipment.level); - if (monsterCollectLevel > 0) - { - var requiredGold = _tableSheets.StakeRegularRewardSheet.OrderedRows - .First(r => r.Level == monsterCollectLevel).RequiredGold; - if (stake) - { - // StakeState; - var stakeStateAddress = StakeState.DeriveAddress(_agentAddress); - var stakeState = new StakeState(stakeStateAddress, 1); - _initialState = _initialState - .SetState(stakeStateAddress, stakeState.SerializeV2()) - .MintAsset(context, stakeStateAddress, requiredGold * _currency); - } - else - { - var mcAddress = MonsterCollectionState.DeriveAddress(_agentAddress, 0); - _initialState = _initialState.SetState( - mcAddress, - new MonsterCollectionState(mcAddress, monsterCollectLevel, 0).Serialize() - ) - .MintAsset(context, mcAddress, requiredGold * _currency); - } - } + _initialState = _initialState + .SetState( + _avatarAddress.Derive(LegacyInventoryKey), + _avatarState.inventory.Serialize() + ) + .SetState( + _avatarAddress.Derive(LegacyWorldInformationKey), + _avatarState.worldInformation.Serialize() + ) + .SetState( + _avatarAddress.Derive(LegacyQuestListKey), + _avatarState.questList.Serialize() + ) + .SetState(_avatarAddress, _avatarState.SerializeV2()); var action = new ItemEnhancement() { itemId = default, - materialId = materialId, + materialIds = materialIds, avatarAddress = _avatarAddress, slotIndex = 0, }; @@ -179,7 +218,7 @@ bool stake PreviousState = _initialState, Signer = _agentAddress, BlockIndex = 1, - Random = new TestRandom(randomSeed), + Random = new TestRandom(), }); var slotState = nextState.GetCombinationSlotState(_avatarAddress, 0); @@ -187,48 +226,72 @@ bool stake var nextAvatarState = nextState.GetAvatarState(_avatarAddress); Assert.Equal(default, resultEquipment.ItemId); Assert.Equal(expectedLevel, resultEquipment.level); - Assert.Equal(expectedGold * _currency, nextState.GetBalance(_agentAddress, _currency)); + Assert.Equal(startExp + expectedExpIncrement, resultEquipment.exp); + Assert.Equal( + (3_000_000 - expectedCost) * _currency, + nextState.GetBalance(_agentAddress, _currency) + ); var arenaSheet = _tableSheets.ArenaSheet; var arenaData = arenaSheet.GetRoundByBlockIndex(1); - var feeStoreAddress = Addresses.GetBlacksmithFeeAddress(arenaData.ChampionshipId, arenaData.Round); + var feeStoreAddress = + Addresses.GetBlacksmithFeeAddress(arenaData.ChampionshipId, arenaData.Round); Assert.Equal( - (1000 - expectedGold) * _currency, + expectedCost * _currency, nextState.GetBalance(feeStoreAddress, _currency) ); Assert.Equal(30, nextAvatarState.mailBox.Count); - var costRow = _tableSheets.EnhancementCostSheetV2 - .OrderedList - .First(x => x.Grade == 1 && x.Level == level + 1); + EnhancementCostSheetV3.Row startRow; + + if (startLevel != 0) + { + startRow = _tableSheets.EnhancementCostSheetV3.OrderedList + .First(x => x.Grade == 1 && x.Level == startLevel); + } + else + { + startRow = new EnhancementCostSheetV3.Row(); + } + + var targetRow = _tableSheets.EnhancementCostSheetV3.OrderedList + .First(x => x.Grade == 1 && x.Level == expectedLevel); var stateDict = (Dictionary)nextState.GetState(slotAddress); var slot = new CombinationSlotState(stateDict); var slotResult = (ItemEnhancement.ResultModel)slot.Result; - Assert.Equal(expected, slotResult.enhancementResult); - - switch (slotResult.enhancementResult) + if (startLevel != expectedLevel) { - case ItemEnhancement.EnhancementResult.GreatSuccess: - var baseAtk = preItemUsable.StatsMap.BaseATK * (costRow.BaseStatGrowthMax.NormalizeFromTenThousandths() + 1); - var extraAtk = preItemUsable.StatsMap.AdditionalATK * (costRow.ExtraStatGrowthMax.NormalizeFromTenThousandths() + 1); - Assert.Equal((int)(baseAtk + extraAtk), resultEquipment.StatsMap.ATK); - break; - case ItemEnhancement.EnhancementResult.Success: - var baseMinAtk = preItemUsable.StatsMap.BaseATK * (costRow.BaseStatGrowthMin.NormalizeFromTenThousandths() + 1); - var baseMaxAtk = preItemUsable.StatsMap.BaseATK * (costRow.BaseStatGrowthMax.NormalizeFromTenThousandths() + 1); - var extraMinAtk = preItemUsable.StatsMap.AdditionalATK * (costRow.ExtraStatGrowthMin.NormalizeFromTenThousandths() + 1); - var extraMaxAtk = preItemUsable.StatsMap.AdditionalATK * (costRow.ExtraStatGrowthMax.NormalizeFromTenThousandths() + 1); - Assert.InRange(resultEquipment.StatsMap.ATK, baseMinAtk + extraMinAtk, baseMaxAtk + extraMaxAtk + 1); - break; - case ItemEnhancement.EnhancementResult.Fail: - Assert.Equal(preItemUsable.StatsMap.ATK, resultEquipment.StatsMap.ATK); - break; + var baseMinAtk = (decimal)preItemUsable.StatsMap.BaseATK; + var baseMaxAtk = (decimal)preItemUsable.StatsMap.BaseATK; + var extraMinAtk = (decimal)preItemUsable.StatsMap.AdditionalATK; + var extraMaxAtk = (decimal)preItemUsable.StatsMap.AdditionalATK; + + for (var i = startLevel + 1; i <= expectedLevel; i++) + { + var currentRow = _tableSheets.EnhancementCostSheetV3.OrderedList + .First(x => + x.Grade == 1 && x.ItemSubType == equipment.ItemSubType && x.Level == i); + + baseMinAtk *= currentRow.BaseStatGrowthMin.NormalizeFromTenThousandths() + 1; + baseMaxAtk *= currentRow.BaseStatGrowthMax.NormalizeFromTenThousandths() + 1; + extraMinAtk *= currentRow.ExtraStatGrowthMin.NormalizeFromTenThousandths() + 1; + extraMaxAtk *= currentRow.ExtraStatGrowthMax.NormalizeFromTenThousandths() + 1; + } + + Assert.InRange( + resultEquipment.StatsMap.ATK, + baseMinAtk + extraMinAtk, + baseMaxAtk + extraMaxAtk + 1 + ); } + Assert.Equal( + expectedBlockIndex + 1, // +1 for execution + resultEquipment.RequiredBlockIndex + ); Assert.Equal(preItemUsable.TradableId, slotResult.preItemUsable.TradableId); Assert.Equal(preItemUsable.TradableId, resultEquipment.TradableId); - Assert.Equal(costRow.Cost, slotResult.gold); - Assert.Equal(expectedCrystal * CrystalCalculator.CRYSTAL, slotResult.CRYSTAL); + Assert.Equal(targetRow.Cost - startRow.Cost, slotResult.gold); } } } diff --git a/.Lib9c.Tests/Action/RapidCombination6Test.cs b/.Lib9c.Tests/Action/RapidCombination6Test.cs index 54c19fd085..a1172cb4b7 100644 --- a/.Lib9c.Tests/Action/RapidCombination6Test.cs +++ b/.Lib9c.Tests/Action/RapidCombination6Test.cs @@ -643,16 +643,16 @@ public void Execute_Throw_InvalidOperationException_When_TargetSlotCreatedBy( case 11: { - Assert.True(ItemEnhancement.TryGetRow( + Assert.True(ItemEnhancement11.TryGetRow( equipment, _tableSheets.EnhancementCostSheetV2, out var costRow)); - var equipmentResult = ItemEnhancement.GetEnhancementResult(costRow, random); + var equipmentResult = ItemEnhancement11.GetEnhancementResult(costRow, random); equipment.LevelUp( random, costRow, - equipmentResult == ItemEnhancement.EnhancementResult.GreatSuccess); - resultModel = new ItemEnhancement.ResultModel + equipmentResult == ItemEnhancement11.EnhancementResult.GreatSuccess); + resultModel = new ItemEnhancement11.ResultModel { id = mailId, preItemUsable = preItemUsable, @@ -660,7 +660,7 @@ public void Execute_Throw_InvalidOperationException_When_TargetSlotCreatedBy( materialItemIdList = new[] { materialEquipment.NonFungibleId }, gold = 0, actionPoint = 0, - enhancementResult = ItemEnhancement.EnhancementResult.GreatSuccess, + enhancementResult = ItemEnhancement11.EnhancementResult.GreatSuccess, CRYSTAL = 0 * CrystalCalculator.CRYSTAL, }; diff --git a/.Lib9c.Tests/Action/RapidCombinationTest7.cs b/.Lib9c.Tests/Action/RapidCombination7Test.cs similarity index 98% rename from .Lib9c.Tests/Action/RapidCombinationTest7.cs rename to .Lib9c.Tests/Action/RapidCombination7Test.cs index 917bfd5687..d61a794d22 100644 --- a/.Lib9c.Tests/Action/RapidCombinationTest7.cs +++ b/.Lib9c.Tests/Action/RapidCombination7Test.cs @@ -20,7 +20,7 @@ namespace Lib9c.Tests.Action using Xunit; using static Lib9c.SerializeKeys; - public class RapidCombinationTest7 + public class RapidCombination7Test { private readonly IAccountStateDelta _initialState; @@ -29,7 +29,7 @@ public class RapidCombinationTest7 private readonly Address _agentAddress; private readonly Address _avatarAddress; - public RapidCombinationTest7() + public RapidCombination7Test() { _initialState = new MockStateDelta(); @@ -642,16 +642,16 @@ public void Execute_NotThrow_InvalidOperationException_When_TargetSlotCreatedBy( case 11: { - Assert.True(ItemEnhancement.TryGetRow( + Assert.True(ItemEnhancement11.TryGetRow( equipment, _tableSheets.EnhancementCostSheetV2, out var costRow)); - var equipmentResult = ItemEnhancement.GetEnhancementResult(costRow, random); + var equipmentResult = ItemEnhancement11.GetEnhancementResult(costRow, random); equipment.LevelUp( random, costRow, - equipmentResult == ItemEnhancement.EnhancementResult.GreatSuccess); - resultModel = new ItemEnhancement.ResultModel + equipmentResult == ItemEnhancement11.EnhancementResult.GreatSuccess); + resultModel = new ItemEnhancement11.ResultModel { id = mailId, preItemUsable = preItemUsable, @@ -659,7 +659,7 @@ public void Execute_NotThrow_InvalidOperationException_When_TargetSlotCreatedBy( materialItemIdList = new[] { materialEquipment.NonFungibleId }, gold = 0, actionPoint = 0, - enhancementResult = ItemEnhancement.EnhancementResult.GreatSuccess, + enhancementResult = ItemEnhancement11.EnhancementResult.GreatSuccess, CRYSTAL = 0 * CrystalCalculator.CRYSTAL, }; diff --git a/.Lib9c.Tests/Action/RapidCombination8Test.cs b/.Lib9c.Tests/Action/RapidCombination8Test.cs index 9666c06142..a0fdb3cc41 100644 --- a/.Lib9c.Tests/Action/RapidCombination8Test.cs +++ b/.Lib9c.Tests/Action/RapidCombination8Test.cs @@ -642,16 +642,16 @@ public void Execute_NotThrow_InvalidOperationException_When_TargetSlotCreatedBy( case 11: { - Assert.True(ItemEnhancement.TryGetRow( + Assert.True(ItemEnhancement11.TryGetRow( equipment, _tableSheets.EnhancementCostSheetV2, out var costRow)); - var equipmentResult = ItemEnhancement.GetEnhancementResult(costRow, random); + var equipmentResult = ItemEnhancement11.GetEnhancementResult(costRow, random); equipment.LevelUp( random, costRow, - equipmentResult == ItemEnhancement.EnhancementResult.GreatSuccess); - resultModel = new ItemEnhancement.ResultModel + equipmentResult == ItemEnhancement11.EnhancementResult.GreatSuccess); + resultModel = new ItemEnhancement11.ResultModel { id = mailId, preItemUsable = preItemUsable, @@ -659,7 +659,7 @@ public void Execute_NotThrow_InvalidOperationException_When_TargetSlotCreatedBy( materialItemIdList = new[] { materialEquipment.NonFungibleId }, gold = 0, actionPoint = 0, - enhancementResult = ItemEnhancement.EnhancementResult.GreatSuccess, + enhancementResult = ItemEnhancement11.EnhancementResult.GreatSuccess, CRYSTAL = 0 * CrystalCalculator.CRYSTAL, }; diff --git a/.Lib9c.Tests/Action/RapidCombinationTest.cs b/.Lib9c.Tests/Action/RapidCombinationTest.cs index a04e0ca5eb..6182af6ed7 100644 --- a/.Lib9c.Tests/Action/RapidCombinationTest.cs +++ b/.Lib9c.Tests/Action/RapidCombinationTest.cs @@ -642,16 +642,16 @@ public void Execute_NotThrow_InvalidOperationException_When_TargetSlotCreatedBy( case 11: { - Assert.True(ItemEnhancement.TryGetRow( + Assert.True(ItemEnhancement11.TryGetRow( equipment, _tableSheets.EnhancementCostSheetV2, out var costRow)); - var equipmentResult = ItemEnhancement.GetEnhancementResult(costRow, random); + var equipmentResult = ItemEnhancement11.GetEnhancementResult(costRow, random); equipment.LevelUp( random, costRow, - equipmentResult == ItemEnhancement.EnhancementResult.GreatSuccess); - resultModel = new ItemEnhancement.ResultModel + equipmentResult == ItemEnhancement11.EnhancementResult.GreatSuccess); + resultModel = new ItemEnhancement11.ResultModel { id = mailId, preItemUsable = preItemUsable, @@ -659,7 +659,7 @@ public void Execute_NotThrow_InvalidOperationException_When_TargetSlotCreatedBy( materialItemIdList = new[] { materialEquipment.NonFungibleId }, gold = 0, actionPoint = 0, - enhancementResult = ItemEnhancement.EnhancementResult.GreatSuccess, + enhancementResult = ItemEnhancement11.EnhancementResult.GreatSuccess, CRYSTAL = 0 * CrystalCalculator.CRYSTAL, }; diff --git a/.Lib9c.Tests/Model/Item/EquipmentTest.cs b/.Lib9c.Tests/Model/Item/EquipmentTest.cs index 3567ddc766..6174c90751 100644 --- a/.Lib9c.Tests/Model/Item/EquipmentTest.cs +++ b/.Lib9c.Tests/Model/Item/EquipmentTest.cs @@ -2,9 +2,11 @@ namespace Lib9c.Tests.Model.Item { using System; using System.Collections.Generic; + using System.Numerics; using Nekoyume.Model.Item; using Nekoyume.TableData; using Xunit; + using static SerializeKeys; public class EquipmentTest { @@ -27,16 +29,24 @@ public static Equipment CreateFirstEquipment( return new Equipment(row, guid == default ? Guid.NewGuid() : guid, requiredBlockIndex); } - [Fact] - public void Serialize() + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(14176747520000)] // Max exp. of equipment + public void Serialize(long exp) { Assert.NotNull(_equipmentRow); var costume = new Equipment(_equipmentRow, Guid.NewGuid(), 0); + costume.exp = exp; var serialized = costume.Serialize(); var deserialized = new Equipment((Bencodex.Types.Dictionary)serialized); var reSerialized = deserialized.Serialize(); + Assert.Equal( + exp != 0, + ((Bencodex.Types.Dictionary)serialized).ContainsKey(EquipmentExpKey) + ); Assert.Equal(costume, deserialized); Assert.Equal(serialized, reSerialized); } diff --git a/.Lib9c.Tests/TableSheets.cs b/.Lib9c.Tests/TableSheets.cs index 96e46ad40e..f334270d82 100644 --- a/.Lib9c.Tests/TableSheets.cs +++ b/.Lib9c.Tests/TableSheets.cs @@ -131,6 +131,8 @@ public TableSheets(Dictionary sheets) public EnhancementCostSheetV2 EnhancementCostSheetV2 { get; private set; } + public EnhancementCostSheetV3 EnhancementCostSheetV3 { get; private set; } + public WeeklyArenaRewardSheet WeeklyArenaRewardSheet { get; internal set; } public CostumeStatSheet CostumeStatSheet { get; private set; } diff --git a/Lib9c.Abstractions/IItemEnhancementV4.cs b/Lib9c.Abstractions/IItemEnhancementV4.cs new file mode 100644 index 0000000000..f5bccd91f7 --- /dev/null +++ b/Lib9c.Abstractions/IItemEnhancementV4.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using Libplanet.Crypto; + +namespace Lib9c.Abstractions +{ + public interface IItemEnhancementV4 + { + Guid ItemId { get; } + List MaterialIds { get; } + Address AvatarAddress { get; } + int SlotIndex { get; } + } +} diff --git a/Lib9c.DevExtensions/Action/CreateOrReplaceAvatar.cs b/Lib9c.DevExtensions/Action/CreateOrReplaceAvatar.cs index 90e84332b3..12448377a3 100644 --- a/Lib9c.DevExtensions/Action/CreateOrReplaceAvatar.cs +++ b/Lib9c.DevExtensions/Action/CreateOrReplaceAvatar.cs @@ -529,7 +529,7 @@ public IAccountStateDelta Execute( } if (eLevel > 0 && - ItemEnhancement.TryGetRow( + ItemEnhancement11.TryGetRow( equipment, enhancementCostSheetV2, out var enhancementCostRow)) diff --git a/Lib9c/Action/AttachmentActionResult.cs b/Lib9c/Action/AttachmentActionResult.cs index 5b1a3a46c4..24927fc514 100644 --- a/Lib9c/Action/AttachmentActionResult.cs +++ b/Lib9c/Action/AttachmentActionResult.cs @@ -21,7 +21,8 @@ private static readonly Dictionary new CombinationConsumable5.ResultModel(d), ["itemEnhancement.result"] = d => new ItemEnhancement7.ResultModel(d), ["item_enhancement9.result"] = d => new ItemEnhancement9.ResultModel(d), - ["item_enhancement11.result"] = d => new ItemEnhancement.ResultModel(d), + ["item_enhancement11.result"] = d => new ItemEnhancement11.ResultModel(d), + ["item_enhancement12.result"] = d => new ItemEnhancement.ResultModel(d), ["sellCancellation.result"] = d => new SellCancellation.Result(d), ["rapidCombination.result"] = d => new RapidCombination0.ResultModel(d), ["rapid_combination5.result"] = d => new RapidCombination5.ResultModel(d), diff --git a/Lib9c/Action/ItemEnhancement.cs b/Lib9c/Action/ItemEnhancement.cs index 186fb9007e..c7547657e6 100644 --- a/Lib9c/Action/ItemEnhancement.cs +++ b/Lib9c/Action/ItemEnhancement.cs @@ -19,39 +19,41 @@ using Nekoyume.TableData; using Nekoyume.TableData.Crystal; using Serilog; - using static Lib9c.SerializeKeys; namespace Nekoyume.Action { /// - /// Updated at https://github.com/planetarium/lib9c/pull/1164 + /// Updated at https://github.com/planetarium/lib9c/pull/2068 /// [Serializable] - [ActionType("item_enhancement11")] - public class ItemEnhancement : GameAction, IItemEnhancementV2 + [ActionType("item_enhancement12")] + public class ItemEnhancement : GameAction, IItemEnhancementV4 { public enum EnhancementResult { - GreatSuccess = 0, + // Result is fixed to Success. + // GreatSuccess = 0, Success = 1, - Fail = 2, + // Fail = 2, } + public const int MaterialCountLimit = 50; + public Guid itemId; - public Guid materialId; + public List materialIds; public Address avatarAddress; public int slotIndex; - Guid IItemEnhancementV2.ItemId => itemId; - Guid IItemEnhancementV2.MaterialId => materialId; - Address IItemEnhancementV2.AvatarAddress => avatarAddress; - int IItemEnhancementV2.SlotIndex => slotIndex; + Guid IItemEnhancementV4.ItemId => itemId; + List IItemEnhancementV4.MaterialIds => materialIds; + Address IItemEnhancementV4.AvatarAddress => avatarAddress; + int IItemEnhancementV4.SlotIndex => slotIndex; [Serializable] public class ResultModel : AttachmentActionResult { - protected override string TypeId => "item_enhancement11.result"; + protected override string TypeId => "item_enhancement12.result"; public Guid id; public IEnumerable materialItemIdList; public BigInteger gold; @@ -101,7 +103,9 @@ protected override IImmutableDictionary PlainValueInternal var dict = new Dictionary { ["itemId"] = itemId.Serialize(), - ["materialId"] = materialId.Serialize(), + ["materialIds"] = new List( + materialIds.OrderBy(i => i).Select(i => i.Serialize()) + ), ["avatarAddress"] = avatarAddress.Serialize(), ["slotIndex"] = slotIndex.Serialize(), }; @@ -113,7 +117,7 @@ protected override IImmutableDictionary PlainValueInternal protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) { itemId = plainValue["itemId"].ToGuid(); - materialId = plainValue["materialId"].ToGuid(); + materialIds = plainValue["materialIds"].ToList(StateExtensions.ToGuid); avatarAddress = plainValue["avatarAddress"].ToAddress(); if (plainValue.TryGetValue((Text)"slotIndex", out var value)) { @@ -126,6 +130,13 @@ public override IAccountStateDelta Execute(IActionContext context) context.UseGas(1); var ctx = context; var states = ctx.PreviousState; + + if (ctx.Rehearsal) + { + return states; + } + + // Collect addresses var slotAddress = avatarAddress.Derive( string.Format( CultureInfo.InvariantCulture, @@ -136,34 +147,44 @@ public override IAccountStateDelta Execute(IActionContext context) var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); var questListAddress = avatarAddress.Derive(LegacyQuestListKey); - - if (ctx.Rehearsal) - { - return states; - } - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); var sw = new Stopwatch(); sw.Start(); var started = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}ItemEnhancement exec started", addressesHex); - if (!states.TryGetAgentAvatarStatesV2(ctx.Signer, avatarAddress, out var agentState, out var avatarState, out _)) + Log.Debug("{AddressesHex} ItemEnhancement exec started", addressesHex); + + // Validate avatar + if (!states.TryGetAgentAvatarStatesV2(ctx.Signer, avatarAddress, out var agentState, + out var avatarState, out var migrationRequired)) + { + throw new FailedLoadStateException( + $"{addressesHex} Aborted as the avatar state of the signer was failed to load." + ); + } + + // Validate AP + var requiredActionPoint = GetRequiredAp(); + if (avatarState.actionPoint < requiredActionPoint) { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); + throw new NotEnoughActionPointException( + $"{addressesHex} Aborted due to insufficient action point: {avatarState.actionPoint} < {requiredActionPoint}" + ); } - if (!avatarState.inventory.TryGetNonFungibleItem(itemId, out ItemUsable enhancementItem)) + // Validate target equipment item + if (!avatarState.inventory.TryGetNonFungibleItem(itemId, + out ItemUsable enhancementItem)) { throw new ItemDoesNotExistException( - $"{addressesHex}Aborted as the NonFungibleItem ({itemId}) was failed to load from avatar's inventory." + $"{addressesHex} Aborted as the NonFungibleItem ({itemId}) was failed to load from avatar's inventory." ); } if (enhancementItem.RequiredBlockIndex > context.BlockIndex) { throw new RequiredBlockIndexException( - $"{addressesHex}Aborted as the equipment to enhance ({itemId}) is not available yet;" + + $"{addressesHex} Aborted as the equipment to enhance ({itemId}) is not available yet;" + $" it will be available at the block #{enhancementItem.RequiredBlockIndex}." ); } @@ -171,194 +192,204 @@ public override IAccountStateDelta Execute(IActionContext context) if (!(enhancementItem is Equipment enhancementEquipment)) { throw new InvalidCastException( - $"{addressesHex}Aborted as the item is not a {nameof(Equipment)}, but {enhancementItem.GetType().Name}." - + $"{addressesHex} Aborted as the item is not a {nameof(Equipment)}, but {enhancementItem.GetType().Name}." ); } + // Validate combination slot var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); if (slotState is null) { - throw new FailedLoadStateException($"{addressesHex}Aborted as the slot state was failed to load. #{slotIndex}"); + throw new FailedLoadStateException( + $"{addressesHex} Aborted as the slot state was failed to load. #{slotIndex}" + ); } if (!slotState.Validate(avatarState, ctx.BlockIndex)) { - throw new CombinationSlotUnlockException($"{addressesHex}Aborted as the slot state was failed to invalid. #{slotIndex}"); + throw new CombinationSlotUnlockException( + $"{addressesHex} Aborted as the slot state was failed to invalid. #{slotIndex}" + ); } + sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Get Equipment: {Elapsed}", addressesHex, sw.Elapsed); + Log.Verbose("{AddressesHex} ItemEnhancement Get Equipment: {Elapsed}", addressesHex, + sw.Elapsed); sw.Restart(); Dictionary sheets = states.GetSheets(sheetTypes: new[] { - typeof(EnhancementCostSheetV2), + typeof(EnhancementCostSheetV3), typeof(MaterialItemSheet), typeof(CrystalEquipmentGrindingSheet), typeof(CrystalMonsterCollectionMultiplierSheet), typeof(StakeRegularRewardSheet) }); - var enhancementCostSheet = sheets.GetSheet(); - if (!TryGetRow(enhancementEquipment, enhancementCostSheet, out var row)) + // Validate from sheet + var enhancementCostSheet = sheets.GetSheet(); + EnhancementCostSheetV3.Row startCostRow; + if (enhancementEquipment.level == 0) + { + startCostRow = new EnhancementCostSheetV3.Row(); + } + else { - throw new SheetRowNotFoundException(addressesHex, nameof(WorldSheet), enhancementEquipment.level); + if (!TryGetRow(enhancementEquipment, enhancementCostSheet, out startCostRow)) + { + throw new SheetRowNotFoundException(addressesHex, nameof(WorldSheet), + enhancementEquipment.level); + } } var maxLevel = GetEquipmentMaxLevel(enhancementEquipment, enhancementCostSheet); - if (enhancementEquipment.level >= maxLevel) + if (enhancementEquipment.level > maxLevel) { throw new EquipmentLevelExceededException( - $"{addressesHex}Aborted due to invalid equipment level: {enhancementEquipment.level} < {maxLevel}"); + $"{addressesHex} Aborted due to invalid equipment level: {enhancementEquipment.level} < {maxLevel}"); } - if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out ItemUsable materialItem)) + // Validate enhancement materials + if (!materialIds.Any() || materialIds.Count > MaterialCountLimit) { - throw new NotEnoughMaterialException( - $"{addressesHex}Aborted as the signer does not have a necessary material ({materialId})." - ); + throw new InvalidItemCountException(); } - if (materialItem.RequiredBlockIndex > context.BlockIndex) - { - throw new RequiredBlockIndexException( - $"{addressesHex}Aborted as the material ({materialId}) is not available yet;" + - $" it will be available at the block #{materialItem.RequiredBlockIndex}." - ); - } + var materialEquipments = new List(); - if (!(materialItem is Equipment materialEquipment)) + foreach (var materialId in materialIds) { - throw new InvalidCastException( - $"{addressesHex}Aborted as the material item is not an {nameof(Equipment)}, but {materialItem.GetType().Name}." - ); - } + if (!avatarState.inventory.TryGetNonFungibleItem(materialId, + out ItemUsable materialItem)) + { + throw new NotEnoughMaterialException( + $"{addressesHex} Aborted as the signer does not have a necessary material ({materialId})." + ); + } - if (enhancementEquipment.ItemId == materialId) - { - throw new InvalidMaterialException( - $"{addressesHex}Aborted as an equipment to enhance ({materialId}) was used as a material too." - ); - } + if (materialItem.RequiredBlockIndex > context.BlockIndex) + { + throw new RequiredBlockIndexException( + $"{addressesHex} Aborted as the material ({materialId}) is not available yet;" + + $" it will be available at the block #{materialItem.RequiredBlockIndex}." + ); + } - if (materialEquipment.ItemSubType != enhancementEquipment.ItemSubType) - { - throw new InvalidMaterialException( - $"{addressesHex}Aborted as the material item is not a {enhancementEquipment.ItemSubType}," + - $" but {materialEquipment.ItemSubType}." - ); - } + if (!(materialItem is Equipment materialEquipment)) + { + throw new InvalidCastException( + $"{addressesHex} Aborted as the material item is not an {nameof(Equipment)}, but {materialItem.GetType().Name}." + ); + } - if (materialEquipment.Grade != enhancementEquipment.Grade) - { - throw new InvalidMaterialException( - $"{addressesHex}Aborted as grades of the equipment to enhance ({enhancementEquipment.Grade})" + - $" and a material ({materialEquipment.Grade}) does not match." - ); - } + if (enhancementEquipment.ItemId == materialId) + { + throw new InvalidMaterialException( + $"{addressesHex} Aborted as an equipment to enhance ({materialId}) was used as a material too." + ); + } - if (materialEquipment.level != enhancementEquipment.level) - { - throw new InvalidMaterialException( - $"{addressesHex}Aborted as levels of the equipment to enhance ({enhancementEquipment.level})" + - $" and a material ({materialEquipment.level}) does not match." - ); + if (materialEquipment.ItemSubType != enhancementEquipment.ItemSubType) + { + throw new InvalidMaterialException( + $"{addressesHex} Aborted as the material item is not a {enhancementEquipment.ItemSubType}," + + $" but {materialEquipment.ItemSubType}." + ); + } + + materialEquipments.Add(materialEquipment); } + sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Get Material: {Elapsed}", addressesHex, sw.Elapsed); + Log.Verbose("{AddressesHex} ItemEnhancement Get Material: {Elapsed}", + addressesHex, sw.Elapsed); sw.Restart(); + + // Do the action // Subtract required action point - var requiredActionPoint = GetRequiredAp(); - if (avatarState.actionPoint < requiredActionPoint) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {requiredActionPoint}" - ); - } avatarState.actionPoint -= requiredActionPoint; - // TransferAsset (NCG) - var requiredNcg = row.Cost; - if (requiredNcg > 0) - { - var arenaSheet = states.GetSheet(); - var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); - var feeStoreAddress = Addresses.GetBlacksmithFeeAddress(arenaData.ChampionshipId, arenaData.Round); - states = states.TransferAsset(ctx, ctx.Signer, feeStoreAddress, states.GetGoldCurrency() * requiredNcg); - } - // Unequip items - materialEquipment.Unequip(); enhancementEquipment.Unequip(); + foreach (var materialEquipment in materialEquipments) + { + materialEquipment.Unequip(); + } - // clone items + // clone enhancement item var preItemUsable = new Equipment((Dictionary)enhancementEquipment.Serialize()); // Equipment level up & Update - var equipmentResult = GetEnhancementResult(row, ctx.Random); - FungibleAssetValue crystal = 0 * CrystalCalculator.CRYSTAL; - if (equipmentResult != EnhancementResult.Fail) + enhancementEquipment.exp += + materialEquipments.Aggregate(0L, (total, m) => total + m.exp); + var row = enhancementCostSheet + .OrderByDescending(r => r.Value.Exp) + .First(row => + row.Value.ItemSubType == enhancementEquipment.ItemSubType && + row.Value.Grade == enhancementEquipment.Grade && + row.Value.Exp <= enhancementEquipment.exp + ).Value; + + if (row.Level > enhancementEquipment.level) { - enhancementEquipment.LevelUp(ctx.Random, row, equipmentResult == EnhancementResult.GreatSuccess); + enhancementEquipment.SetLevel(ctx.Random, row.Level, enhancementCostSheet); + } + + EnhancementCostSheetV3.Row targetCostRow; + if (enhancementEquipment.level == 0) + { + targetCostRow = new EnhancementCostSheetV3.Row(); } else { - Address monsterCollectionAddress = MonsterCollectionState.DeriveAddress( - context.Signer, - agentState.MonsterCollectionRound - ); - - Currency currency = states.GetGoldCurrency(); - FungibleAssetValue stakedAmount = 0 * currency; - if (states.TryGetStakeState(context.Signer, out StakeState stakeState)) + if (!TryGetRow(enhancementEquipment, enhancementCostSheet, out targetCostRow)) { - stakedAmount = states.GetBalance(stakeState.address, currency); + throw new SheetRowNotFoundException(addressesHex, nameof(WorldSheet), + enhancementEquipment.level); } - else - { - if (states.TryGetState(monsterCollectionAddress, out Dictionary _)) - { - stakedAmount = states.GetBalance(monsterCollectionAddress, currency); - } - } - - crystal = CrystalCalculator.CalculateCrystal( - context.Signer, - new[] { preItemUsable }, - stakedAmount, - true, - sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet() - ); + } - if (crystal > 0 * CrystalCalculator.CRYSTAL) - { - states = states.MintAsset(context, context.Signer, crystal); - } + // TransferAsset (NCG) + // Total cost = Total cost to reach target level - total cost to reach start level (already used) + var requiredNcg = targetCostRow.Cost - startCostRow.Cost; + if (requiredNcg > 0) + { + var arenaSheet = states.GetSheet(); + var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); + var feeStoreAddress = + Addresses.GetBlacksmithFeeAddress(arenaData.ChampionshipId, arenaData.Round); + states = states.TransferAsset(ctx, ctx.Signer, feeStoreAddress, + states.GetGoldCurrency() * requiredNcg); } - var requiredBlockCount = GetRequiredBlockCount(row, equipmentResult); + // Required block index = Sum of all required blocks from start level to target level + var requiredBlockCount = GetRequiredBlockCount(preItemUsable, enhancementEquipment, + enhancementCostSheet); var requiredBlockIndex = ctx.BlockIndex + requiredBlockCount; enhancementEquipment.Update(requiredBlockIndex); - // Remove material - avatarState.inventory.RemoveNonFungibleItem(materialId); + // Remove materials + foreach (var materialId in materialIds) + { + avatarState.inventory.RemoveNonFungibleItem(materialId); + } + sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Upgrade Equipment: {Elapsed}", addressesHex, sw.Elapsed); + Log.Verbose("{AddressesHex} ItemEnhancement Upgrade Equipment: {Elapsed}", addressesHex, + sw.Elapsed); // Send scheduled mail var result = new ResultModel { preItemUsable = preItemUsable, itemUsable = enhancementEquipment, - materialItemIdList = new[] { materialId }, + materialItemIdList = materialIds.ToArray(), actionPoint = requiredActionPoint, - enhancementResult = equipmentResult, + enhancementResult = EnhancementResult.Success, // Result is fixed to Success gold = requiredNcg, - CRYSTAL = crystal, + CRYSTAL = 0 * CrystalCalculator.CRYSTAL, }; var mail = new ItemEnhanceMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), requiredBlockIndex); @@ -378,52 +409,47 @@ public override IAccountStateDelta Execute(IActionContext context) sw.Restart(); states = states .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(avatarAddress, avatarState.SerializeV2()); - sw.Stop(); - Log.Verbose("{AddressesHex}ItemEnhancement Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - var ended = DateTimeOffset.UtcNow; - Log.Debug("{AddressesHex}ItemEnhancement Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states.SetState(slotAddress, slotState.Serialize()); - } - - public static EnhancementResult GetEnhancementResult(EnhancementCostSheetV2.Row row, IRandom random) - { - var rand = random.Next(1, GameConfig.MaximumProbability + 1); - if (rand <= row.GreatSuccessRatio) + .SetState(questListAddress, avatarState.questList.Serialize()); + if (migrationRequired) { - return EnhancementResult.GreatSuccess; + states = states + .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) + .SetState(avatarAddress, avatarState.SerializeV2()); } - return rand <= row.GreatSuccessRatio + row.SuccessRatio ? EnhancementResult.Success : EnhancementResult.Fail; + sw.Stop(); + Log.Verbose("{AddressesHex} ItemEnhancement Set AvatarState: {Elapsed}", addressesHex, + sw.Elapsed); + var ended = DateTimeOffset.UtcNow; + Log.Debug("{AddressesHex} ItemEnhancement Total Executed Time: {Elapsed}", addressesHex, + ended - started); + return states.SetState(slotAddress, slotState.Serialize()); } - public static int GetRequiredBlockCount(EnhancementCostSheetV2.Row row, EnhancementResult result) + public static int GetRequiredBlockCount(Equipment preEquipment, Equipment targetEquipment, + EnhancementCostSheetV3 sheet) { - switch (result) - { - case EnhancementResult.GreatSuccess: - return row.GreatSuccessRequiredBlockIndex; - case EnhancementResult.Success: - return row.SuccessRequiredBlockIndex; - case EnhancementResult.Fail: - return row.FailRequiredBlockIndex; - default: - throw new ArgumentOutOfRangeException(nameof(result), result, null); - } + return sheet.OrderedList + .Where(e => + e.ItemSubType == targetEquipment.ItemSubType && + e.Grade == targetEquipment.Grade && + e.Level > preEquipment.level && + e.Level <= targetEquipment.level) + .Aggregate(0, (blocks, row) => blocks + row.RequiredBlockIndex); } - public static bool TryGetRow(Equipment equipment, EnhancementCostSheetV2 sheet, out EnhancementCostSheetV2.Row row) + public static bool TryGetRow(Equipment equipment, EnhancementCostSheetV3 sheet, + out EnhancementCostSheetV3.Row row) { - var grade = equipment.Grade; - var level = equipment.level + 1; - var itemSubType = equipment.ItemSubType; - row = sheet.OrderedList.FirstOrDefault(x => x.Grade == grade && x.Level == level && x.ItemSubType == itemSubType); + row = sheet.OrderedList.FirstOrDefault(x => + x.Grade == equipment.Grade && + x.Level == equipment.level && + x.ItemSubType == equipment.ItemSubType + ); return row != null; } - public static int GetEquipmentMaxLevel(Equipment equipment, EnhancementCostSheetV2 sheet) + public static int GetEquipmentMaxLevel(Equipment equipment, EnhancementCostSheetV3 sheet) { return sheet.OrderedList.Where(x => x.Grade == equipment.Grade).Max(x => x.Level); } diff --git a/Lib9c/Action/ItemEnhancement11.cs b/Lib9c/Action/ItemEnhancement11.cs new file mode 100644 index 0000000000..7a25cfb413 --- /dev/null +++ b/Lib9c/Action/ItemEnhancement11.cs @@ -0,0 +1,436 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Numerics; +using Bencodex.Types; +using Lib9c.Abstractions; +using Libplanet.Action; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Libplanet.Types.Assets; +using Nekoyume.Extensions; +using Nekoyume.Helper; +using Nekoyume.Model.Item; +using Nekoyume.Model.Mail; +using Nekoyume.Model.State; +using Nekoyume.TableData; +using Nekoyume.TableData.Crystal; +using Serilog; + +using static Lib9c.SerializeKeys; + +namespace Nekoyume.Action +{ + /// + /// Updated at https://github.com/planetarium/lib9c/pull/1164 + /// + [Serializable] + [ActionType("item_enhancement11")] + public class ItemEnhancement11 : GameAction, IItemEnhancementV2 + { + public enum EnhancementResult + { + GreatSuccess = 0, + Success = 1, + Fail = 2, + } + + public Guid itemId; + public Guid materialId; + public Address avatarAddress; + public int slotIndex; + + Guid IItemEnhancementV2.ItemId => itemId; + Guid IItemEnhancementV2.MaterialId => materialId; + Address IItemEnhancementV2.AvatarAddress => avatarAddress; + int IItemEnhancementV2.SlotIndex => slotIndex; + + [Serializable] + public class ResultModel : AttachmentActionResult + { + protected override string TypeId => "item_enhancement11.result"; + public Guid id; + public IEnumerable materialItemIdList; + public BigInteger gold; + public int actionPoint; + public EnhancementResult enhancementResult; + public ItemUsable preItemUsable; + public FungibleAssetValue CRYSTAL; + + public ResultModel() + { + } + + public ResultModel(Dictionary serialized) : base(serialized) + { + id = serialized["id"].ToGuid(); + materialItemIdList = serialized["materialItemIdList"].ToList(StateExtensions.ToGuid); + gold = serialized["gold"].ToBigInteger(); + actionPoint = serialized["actionPoint"].ToInteger(); + enhancementResult = serialized["enhancementResult"].ToEnum(); + preItemUsable = serialized.ContainsKey("preItemUsable") + ? (ItemUsable)ItemFactory.Deserialize((Dictionary)serialized["preItemUsable"]) + : null; + CRYSTAL = serialized["c"].ToFungibleAssetValue(); + } + + public override IValue Serialize() => +#pragma warning disable LAA1002 + new Dictionary(new Dictionary + { + [(Text)"id"] = id.Serialize(), + [(Text)"materialItemIdList"] = materialItemIdList + .OrderBy(i => i) + .Select(g => g.Serialize()).Serialize(), + [(Text)"gold"] = gold.Serialize(), + [(Text)"actionPoint"] = actionPoint.Serialize(), + [(Text)"enhancementResult"] = enhancementResult.Serialize(), + [(Text)"preItemUsable"] = preItemUsable.Serialize(), + [(Text)"c"] = CRYSTAL.Serialize(), + }.Union((Dictionary)base.Serialize())); +#pragma warning restore LAA1002 + } + + protected override IImmutableDictionary PlainValueInternal + { + get + { + var dict = new Dictionary + { + ["itemId"] = itemId.Serialize(), + ["materialId"] = materialId.Serialize(), + ["avatarAddress"] = avatarAddress.Serialize(), + ["slotIndex"] = slotIndex.Serialize(), + }; + + return dict.ToImmutableDictionary(); + } + } + + protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) + { + itemId = plainValue["itemId"].ToGuid(); + materialId = plainValue["materialId"].ToGuid(); + avatarAddress = plainValue["avatarAddress"].ToAddress(); + if (plainValue.TryGetValue((Text)"slotIndex", out var value)) + { + slotIndex = value.ToInteger(); + } + } + + public override IAccountStateDelta Execute(IActionContext context) + { + context.UseGas(1); + var ctx = context; + var states = ctx.PreviousState; + var slotAddress = avatarAddress.Derive( + string.Format( + CultureInfo.InvariantCulture, + CombinationSlotState.DeriveFormat, + slotIndex + ) + ); + var inventoryAddress = avatarAddress.Derive(LegacyInventoryKey); + var worldInformationAddress = avatarAddress.Derive(LegacyWorldInformationKey); + var questListAddress = avatarAddress.Derive(LegacyQuestListKey); + + if (ctx.Rehearsal) + { + return states; + } + + var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); + + var sw = new Stopwatch(); + sw.Start(); + var started = DateTimeOffset.UtcNow; + Log.Debug("{AddressesHex}ItemEnhancement exec started", addressesHex); + if (!states.TryGetAgentAvatarStatesV2(ctx.Signer, avatarAddress, out var agentState, out var avatarState, out _)) + { + throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); + } + + if (!avatarState.inventory.TryGetNonFungibleItem(itemId, out ItemUsable enhancementItem)) + { + throw new ItemDoesNotExistException( + $"{addressesHex}Aborted as the NonFungibleItem ({itemId}) was failed to load from avatar's inventory." + ); + } + + if (enhancementItem.RequiredBlockIndex > context.BlockIndex) + { + throw new RequiredBlockIndexException( + $"{addressesHex}Aborted as the equipment to enhance ({itemId}) is not available yet;" + + $" it will be available at the block #{enhancementItem.RequiredBlockIndex}." + ); + } + + if (!(enhancementItem is Equipment enhancementEquipment)) + { + throw new InvalidCastException( + $"{addressesHex}Aborted as the item is not a {nameof(Equipment)}, but {enhancementItem.GetType().Name}." + + ); + } + + var slotState = states.GetCombinationSlotState(avatarAddress, slotIndex); + if (slotState is null) + { + throw new FailedLoadStateException($"{addressesHex}Aborted as the slot state was failed to load. #{slotIndex}"); + } + + if (!slotState.Validate(avatarState, ctx.BlockIndex)) + { + throw new CombinationSlotUnlockException($"{addressesHex}Aborted as the slot state was failed to invalid. #{slotIndex}"); + } + sw.Stop(); + Log.Verbose("{AddressesHex}ItemEnhancement Get Equipment: {Elapsed}", addressesHex, sw.Elapsed); + + sw.Restart(); + + Dictionary sheets = states.GetSheets(sheetTypes: new[] + { + typeof(EnhancementCostSheetV2), + typeof(MaterialItemSheet), + typeof(CrystalEquipmentGrindingSheet), + typeof(CrystalMonsterCollectionMultiplierSheet), + typeof(StakeRegularRewardSheet) + }); + + var enhancementCostSheet = sheets.GetSheet(); + if (!TryGetRow(enhancementEquipment, enhancementCostSheet, out var row)) + { + throw new SheetRowNotFoundException(addressesHex, nameof(WorldSheet), enhancementEquipment.level); + } + + var maxLevel = GetEquipmentMaxLevel(enhancementEquipment, enhancementCostSheet); + if (enhancementEquipment.level >= maxLevel) + { + throw new EquipmentLevelExceededException( + $"{addressesHex}Aborted due to invalid equipment level: {enhancementEquipment.level} < {maxLevel}"); + } + + if (!avatarState.inventory.TryGetNonFungibleItem(materialId, out ItemUsable materialItem)) + { + throw new NotEnoughMaterialException( + $"{addressesHex}Aborted as the signer does not have a necessary material ({materialId})." + ); + } + + if (materialItem.RequiredBlockIndex > context.BlockIndex) + { + throw new RequiredBlockIndexException( + $"{addressesHex}Aborted as the material ({materialId}) is not available yet;" + + $" it will be available at the block #{materialItem.RequiredBlockIndex}." + ); + } + + if (!(materialItem is Equipment materialEquipment)) + { + throw new InvalidCastException( + $"{addressesHex}Aborted as the material item is not an {nameof(Equipment)}, but {materialItem.GetType().Name}." + ); + } + + if (enhancementEquipment.ItemId == materialId) + { + throw new InvalidMaterialException( + $"{addressesHex}Aborted as an equipment to enhance ({materialId}) was used as a material too." + ); + } + + if (materialEquipment.ItemSubType != enhancementEquipment.ItemSubType) + { + throw new InvalidMaterialException( + $"{addressesHex}Aborted as the material item is not a {enhancementEquipment.ItemSubType}," + + $" but {materialEquipment.ItemSubType}." + ); + } + + if (materialEquipment.Grade != enhancementEquipment.Grade) + { + throw new InvalidMaterialException( + $"{addressesHex}Aborted as grades of the equipment to enhance ({enhancementEquipment.Grade})" + + $" and a material ({materialEquipment.Grade}) does not match." + ); + } + + if (materialEquipment.level != enhancementEquipment.level) + { + throw new InvalidMaterialException( + $"{addressesHex}Aborted as levels of the equipment to enhance ({enhancementEquipment.level})" + + $" and a material ({materialEquipment.level}) does not match." + ); + } + sw.Stop(); + Log.Verbose("{AddressesHex}ItemEnhancement Get Material: {Elapsed}", addressesHex, sw.Elapsed); + + sw.Restart(); + // Subtract required action point + var requiredActionPoint = GetRequiredAp(); + if (avatarState.actionPoint < requiredActionPoint) + { + throw new NotEnoughActionPointException( + $"{addressesHex}Aborted due to insufficient action point: {avatarState.actionPoint} < {requiredActionPoint}" + ); + } + avatarState.actionPoint -= requiredActionPoint; + + // TransferAsset (NCG) + var requiredNcg = row.Cost; + if (requiredNcg > 0) + { + var arenaSheet = states.GetSheet(); + var arenaData = arenaSheet.GetRoundByBlockIndex(context.BlockIndex); + var feeStoreAddress = Addresses.GetBlacksmithFeeAddress(arenaData.ChampionshipId, arenaData.Round); + states = states.TransferAsset(ctx, ctx.Signer, feeStoreAddress, states.GetGoldCurrency() * requiredNcg); + } + + // Unequip items + materialEquipment.Unequip(); + enhancementEquipment.Unequip(); + + // clone items + var preItemUsable = new Equipment((Dictionary)enhancementEquipment.Serialize()); + + // Equipment level up & Update + var equipmentResult = GetEnhancementResult(row, ctx.Random); + FungibleAssetValue crystal = 0 * CrystalCalculator.CRYSTAL; + if (equipmentResult != EnhancementResult.Fail) + { + enhancementEquipment.LevelUp(ctx.Random, row, equipmentResult == EnhancementResult.GreatSuccess); + } + else + { + Address monsterCollectionAddress = MonsterCollectionState.DeriveAddress( + context.Signer, + agentState.MonsterCollectionRound + ); + + Currency currency = states.GetGoldCurrency(); + FungibleAssetValue stakedAmount = 0 * currency; + if (states.TryGetStakeState(context.Signer, out StakeState stakeState)) + { + stakedAmount = states.GetBalance(stakeState.address, currency); + } + else + { + if (states.TryGetState(monsterCollectionAddress, out Dictionary _)) + { + stakedAmount = states.GetBalance(monsterCollectionAddress, currency); + } + } + + crystal = CrystalCalculator.CalculateCrystal( + context.Signer, + new[] { preItemUsable }, + stakedAmount, + true, + sheets.GetSheet(), + sheets.GetSheet(), + sheets.GetSheet() + ); + + if (crystal > 0 * CrystalCalculator.CRYSTAL) + { + states = states.MintAsset(context, context.Signer, crystal); + } + } + + var requiredBlockCount = GetRequiredBlockCount(row, equipmentResult); + var requiredBlockIndex = ctx.BlockIndex + requiredBlockCount; + enhancementEquipment.Update(requiredBlockIndex); + + // Remove material + avatarState.inventory.RemoveNonFungibleItem(materialId); + sw.Stop(); + Log.Verbose("{AddressesHex}ItemEnhancement Upgrade Equipment: {Elapsed}", addressesHex, sw.Elapsed); + + // Send scheduled mail + var result = new ResultModel + { + preItemUsable = preItemUsable, + itemUsable = enhancementEquipment, + materialItemIdList = new[] { materialId }, + actionPoint = requiredActionPoint, + enhancementResult = equipmentResult, + gold = requiredNcg, + CRYSTAL = crystal, + }; + + var mail = new ItemEnhanceMail(result, ctx.BlockIndex, ctx.Random.GenerateRandomGuid(), requiredBlockIndex); + result.id = mail.id; + avatarState.inventory.RemoveNonFungibleItem(enhancementEquipment); + avatarState.Update(mail); + avatarState.UpdateFromItemEnhancement(enhancementEquipment); + + // Update quest reward + var materialSheet = sheets.GetSheet(); + avatarState.UpdateQuestRewards(materialSheet); + + // Update slot state + slotState.Update(result, ctx.BlockIndex, requiredBlockIndex); + + // Set state + sw.Restart(); + states = states + .SetState(inventoryAddress, avatarState.inventory.Serialize()) + .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) + .SetState(questListAddress, avatarState.questList.Serialize()) + .SetState(avatarAddress, avatarState.SerializeV2()); + sw.Stop(); + Log.Verbose("{AddressesHex}ItemEnhancement Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); + var ended = DateTimeOffset.UtcNow; + Log.Debug("{AddressesHex}ItemEnhancement Total Executed Time: {Elapsed}", addressesHex, ended - started); + return states.SetState(slotAddress, slotState.Serialize()); + } + + public static EnhancementResult GetEnhancementResult(EnhancementCostSheetV2.Row row, IRandom random) + { + var rand = random.Next(1, GameConfig.MaximumProbability + 1); + if (rand <= row.GreatSuccessRatio) + { + return EnhancementResult.GreatSuccess; + } + + return rand <= row.GreatSuccessRatio + row.SuccessRatio ? EnhancementResult.Success : EnhancementResult.Fail; + } + + public static int GetRequiredBlockCount(EnhancementCostSheetV2.Row row, EnhancementResult result) + { + switch (result) + { + case EnhancementResult.GreatSuccess: + return row.GreatSuccessRequiredBlockIndex; + case EnhancementResult.Success: + return row.SuccessRequiredBlockIndex; + case EnhancementResult.Fail: + return row.FailRequiredBlockIndex; + default: + throw new ArgumentOutOfRangeException(nameof(result), result, null); + } + } + + public static bool TryGetRow(Equipment equipment, EnhancementCostSheetV2 sheet, out EnhancementCostSheetV2.Row row) + { + var grade = equipment.Grade; + var level = equipment.level + 1; + var itemSubType = equipment.ItemSubType; + row = sheet.OrderedList.FirstOrDefault(x => x.Grade == grade && x.Level == level && x.ItemSubType == itemSubType); + return row != null; + } + + public static int GetEquipmentMaxLevel(Equipment equipment, EnhancementCostSheetV2 sheet) + { + return sheet.OrderedList.Where(x => x.Grade == equipment.Grade).Max(x => x.Level); + } + + public static int GetRequiredAp() + { + return GameConfig.EnhanceEquipmentCostAP; + } + } +} diff --git a/Lib9c/Extensions/CombinationSlotStateExtensions.cs b/Lib9c/Extensions/CombinationSlotStateExtensions.cs index 04561cc687..9724b67a2b 100644 --- a/Lib9c/Extensions/CombinationSlotStateExtensions.cs +++ b/Lib9c/Extensions/CombinationSlotStateExtensions.cs @@ -59,6 +59,9 @@ public static bool TryGetResultId(this CombinationSlotState state, out Guid resu case ItemEnhancement10.ResultModel r: resultId = r.id; break; + case ItemEnhancement11.ResultModel r: + resultId = r.id; + break; case MonsterCollectionResult r: resultId = r.id; break; @@ -111,6 +114,9 @@ public static bool TryGetResultIdV1(this CombinationSlotState state, out Guid re case ItemEnhancement7.ResultModel r: resultId = r.id; break; + case ItemEnhancement11.ResultModel r: + resultId = r.id; + break; case MonsterCollectionResult r: resultId = r.id; break; @@ -182,6 +188,13 @@ public static bool TryGetMail( resultId, requiredBlockIndex); return true; + case ItemEnhancement11.ResultModel r: + itemEnhanceMail = new ItemEnhanceMail( + r, + blockIndex, + resultId, + requiredBlockIndex); + return true; case CombinationConsumable5.ResultModel r: combinationMail = new CombinationMail( r, @@ -226,6 +239,13 @@ public static bool TryGetMailV1( resultId, requiredBlockIndex); return true; + case ItemEnhancement11.ResultModel r: + itemEnhanceMail = new ItemEnhanceMail( + r, + blockIndex, + resultId, + requiredBlockIndex); + return true; case CombinationConsumable5.ResultModel r: combinationMail = new CombinationMail( r, diff --git a/Lib9c/Model/Item/Equipment.cs b/Lib9c/Model/Item/Equipment.cs index 9bca8623a5..37a0dfed67 100644 --- a/Lib9c/Model/Item/Equipment.cs +++ b/Lib9c/Model/Item/Equipment.cs @@ -4,7 +4,6 @@ using System.Runtime.Serialization; using Bencodex.Types; using Libplanet.Action; -using Nekoyume.Action; using Nekoyume.Extensions; using Nekoyume.Model.Stat; using Nekoyume.Model.State; @@ -19,6 +18,7 @@ public class Equipment : ItemUsable, IEquippableItem // FIXME: Whether the equipment is equipped or not has no asset value and must be removed from the state. public bool equipped; public int level; + public long exp; public int optionCountFromCombination; public DecimalStat Stat { get; } @@ -62,6 +62,18 @@ public Equipment(Dictionary serialized) : base(serialized) } } + if (serialized.TryGetValue((Text)EquipmentExpKey, out value)) + { + try + { + exp = value.ToLong(); + } + catch (InvalidCastException) + { + exp = (long)((Integer)value).Value; + } + } + if (serialized.TryGetValue((Text) LegacyStatKey, out value)) { Stat = value.ToDecimalStat(); @@ -113,6 +125,11 @@ public override IValue Serialize() dict = dict.SetItem(MadeWithMimisbrunnrRecipeKey, MadeWithMimisbrunnrRecipe.Serialize()); } + if (exp > 0) + { + dict = dict.SetItem(EquipmentExpKey, exp.Serialize()); + } + return dict; #pragma warning restore LAA1002 } @@ -140,6 +157,7 @@ public void LevelUpV1() } } + [Obsolete("Since ItemEnhancement12, Use `SetLevel` instead.")] public void LevelUp(IRandom random, EnhancementCostSheetV2.Row row, bool isGreatSuccess) { level++; @@ -160,6 +178,32 @@ public void LevelUp(IRandom random, EnhancementCostSheetV2.Row row, bool isGreat } } + public void SetLevel(IRandom random, int targetLevel, EnhancementCostSheetV3 sheet) + { + var startLevel = level; + level = targetLevel; + for (var i = startLevel + 1; i <= targetLevel; i++) + { + var row = sheet.OrderedList.First( + r => r.Level == i && r.Grade == Grade && r.ItemSubType == ItemSubType + ); + var rand = random.Next(row.BaseStatGrowthMin, row.BaseStatGrowthMax + 1); + var ratio = rand.NormalizeFromTenThousandths(); + var baseStat = StatsMap.GetBaseStat(UniqueStatType) * ratio; + if (baseStat > 0) + { + baseStat = Math.Max(1.0m, baseStat); + } + + StatsMap.AddStatValue(UniqueStatType, baseStat); + + if (GetOptionCount() > 0) + { + UpdateOptionsV3(random, row); + } + } + } + public List GetOptions() { var options = new List(); @@ -197,6 +241,7 @@ private void UpdateOptions() } } + [Obsolete("Since ItemEnhancement12, Use UpdateOptionV3 instead.")] private void UpdateOptionsV2(IRandom random, EnhancementCostSheetV2.Row row, bool isGreatSuccess) { foreach (var stat in StatsMap.GetAdditionalStats()) @@ -236,6 +281,59 @@ private void UpdateOptionsV2(IRandom random, EnhancementCostSheetV2.Row row, boo { addPower = Math.Max(1.0m, addPower); } + + var addStatPowerRatio = skill.StatPowerRatio * damageRatio; + if (addStatPowerRatio > 0) + { + addStatPowerRatio = Math.Max(1.0m, addStatPowerRatio); + } + + var chance = skill.Chance + (int)addChance; + var power = skill.Power + (int)addPower; + var statPowerRatio = skill.StatPowerRatio + (int)addStatPowerRatio; + + skill.Update(chance, power, statPowerRatio); + } + } + + private void UpdateOptionsV3(IRandom random, EnhancementCostSheetV3.Row row) + { + foreach (var stat in StatsMap.GetAdditionalStats()) + { + var rand = random.Next(row.ExtraStatGrowthMin, row.ExtraStatGrowthMax + 1); + var ratio = rand.NormalizeFromTenThousandths(); + var addValue = stat.AdditionalValue * ratio; + if (addValue > 0) + { + addValue = Math.Max(1.0m, addValue); + } + + StatsMap.SetStatAdditionalValue(stat.StatType, stat.AdditionalValue + addValue); + } + + var skills = new List(); + skills.AddRange(Skills); + skills.AddRange(BuffSkills); + foreach (var skill in skills) + { + var chanceRand = random.Next(row.ExtraSkillChanceGrowthMin, + row.ExtraSkillChanceGrowthMax + 1); + var chanceRatio = chanceRand.NormalizeFromTenThousandths(); + var addChance = skill.Chance * chanceRatio; + if (addChance > 0) + { + addChance = Math.Max(1.0m, addChance); + } + + var damageRand = random.Next(row.ExtraSkillDamageGrowthMin, + row.ExtraSkillDamageGrowthMax + 1); + var damageRatio = damageRand.NormalizeFromTenThousandths(); + var addPower = skill.Power * damageRatio; + if (addPower > 0) + { + addPower = Math.Max(1.0m, addPower); + } + var addStatPowerRatio = skill.StatPowerRatio * damageRatio; if (addStatPowerRatio > 0) { @@ -253,7 +351,8 @@ private void UpdateOptionsV2(IRandom random, EnhancementCostSheetV2.Row row, boo protected bool Equals(Equipment other) { return base.Equals(other) && equipped == other.equipped && level == other.level && - Equals(Stat, other.Stat) && SetId == other.SetId && SpineResourcePath == other.SpineResourcePath; + exp == other.exp && Equals(Stat, other.Stat) && SetId == other.SetId && + SpineResourcePath == other.SpineResourcePath; } public override bool Equals(object obj) diff --git a/Lib9c/SerializeKeys.cs b/Lib9c/SerializeKeys.cs index bcb208d19b..f1e5daebab 100644 --- a/Lib9c/SerializeKeys.cs +++ b/Lib9c/SerializeKeys.cs @@ -44,6 +44,7 @@ public static class SerializeKeys public const string OptionCountFromCombinationKey = "oc"; public const string RequiredCharacterLevelKey = "rc"; public const string MadeWithMimisbrunnrRecipeKey = "mwmr"; + public const string EquipmentExpKey = "eq_exp"; // Stat public const string StatTypeKey = "stt"; diff --git a/Lib9c/TableCSV/Cost/EnhancementCostSheetV3.csv b/Lib9c/TableCSV/Cost/EnhancementCostSheetV3.csv new file mode 100644 index 0000000000..0cd2fe44c7 --- /dev/null +++ b/Lib9c/TableCSV/Cost/EnhancementCostSheetV3.csv @@ -0,0 +1,526 @@ +id,item_sub_type,grade,level,cost,exp,required_block_index,base_stat_growth_min,base_stat_growth_max,extra_stat_growth_min,extra_stat_growth_max,extra_skill_damage_growth_min,extra_skill_damage_growth_max,extra_skill_chance_growth_min,extra_skill_chance_growth_max +1,Weapon,1,1,0,20,25,800,1200,0,0,0,0,0,0 +2,Weapon,1,2,0,40,62,800,1200,0,0,0,0,0,0 +3,Weapon,1,3,0,80,125,800,1200,0,0,0,0,0,0 +4,Weapon,1,4,20,160,625,800,1200,2500,3500,2500,3500,1200,1800 +5,Weapon,1,5,40,320,925,800,1200,0,0,0,0,0,0 +6,Weapon,1,6,80,640,1300,800,1200,0,0,0,0,0,0 +7,Weapon,1,7,160,1280,2500,800,1200,2500,3500,2500,3500,1200,1800 +8,Weapon,1,8,320,2560,2975,800,1200,600,1000,600,1000,400,600 +9,Weapon,1,9,640,5120,3500,800,1200,600,1000,600,1000,400,600 +10,Weapon,1,10,1280,10240,4750,800,1200,1100,1700,1100,1700,700,1100 +11,Weapon,1,11,2560,20480,5000,800,1200,600,1000,600,1000,400,600 +12,Weapon,1,12,5120,40960,5250,800,1200,600,1000,600,1000,400,600 +13,Weapon,1,13,10240,81920,5500,800,1200,1100,1700,1100,1700,700,1100 +14,Weapon,1,14,20480,163840,5750,800,1200,600,1000,600,1000,400,600 +15,Weapon,1,15,40960,327680,6000,800,1200,600,1000,600,1000,400,600 +16,Weapon,1,16,81920,655360,6250,800,1200,1100,1700,1100,1700,700,1100 +17,Weapon,1,17,163840,1310720,6500,800,1200,600,1000,600,1000,400,600 +18,Weapon,1,18,327680,2621440,6750,800,1200,600,1000,600,1000,400,600 +19,Weapon,1,19,655360,5242880,7000,800,1200,1100,1700,1100,1700,700,1100 +20,Weapon,1,20,1310720,10485760,7250,800,1200,800,1200,800,1200,400,600 +21,Weapon,1,21,2621440,20971520,7500,800,1200,800,1200,800,1200,400,600 +22,Weapon,2,1,0,160,37,800,1200,0,0,0,0,0,0 +23,Weapon,2,2,0,320,93,800,1200,0,0,0,0,0,0 +24,Weapon,2,3,0,640,187,800,1200,0,0,0,0,0,0 +25,Weapon,2,4,20,1280,937,800,1200,2500,3500,2500,3500,1200,1800 +26,Weapon,2,5,40,2560,1387,800,1200,0,0,0,0,0,0 +27,Weapon,2,6,80,5120,1950,800,1200,0,0,0,0,0,0 +28,Weapon,2,7,160,10240,3750,800,1200,2500,3500,2500,3500,1200,1800 +29,Weapon,2,8,320,20480,4462,800,1200,600,1000,600,1000,400,600 +30,Weapon,2,9,640,40960,5250,800,1200,600,1000,600,1000,400,600 +31,Weapon,2,10,1280,81920,7125,800,1200,1100,1700,1100,1700,700,1100 +32,Weapon,2,11,2560,163840,7500,800,1200,600,1000,600,1000,400,600 +33,Weapon,2,12,5120,327680,7875,800,1200,600,1000,600,1000,400,600 +34,Weapon,2,13,10240,655360,8250,800,1200,1100,1700,1100,1700,700,1100 +35,Weapon,2,14,20480,1310720,8625,800,1200,600,1000,600,1000,400,600 +36,Weapon,2,15,40960,2621440,9000,800,1200,600,1000,600,1000,400,600 +37,Weapon,2,16,81920,5242880,9375,800,1200,1100,1700,1100,1700,700,1100 +38,Weapon,2,17,163840,10485760,9750,800,1200,600,1000,600,1000,400,600 +39,Weapon,2,18,327680,20971520,10125,800,1200,600,1000,600,1000,400,600 +40,Weapon,2,19,655360,41943040,10500,800,1200,1100,1700,1100,1700,700,1100 +41,Weapon,2,20,1310720,83886080,10875,800,1200,800,1200,800,1200,400,600 +42,Weapon,2,21,2621440,167772160,11250,800,1200,800,1200,800,1200,400,600 +43,Weapon,3,1,0,32040,75,800,1200,0,0,0,0,0,0 +44,Weapon,3,2,0,64080,187,800,1200,0,0,0,0,0,0 +45,Weapon,3,3,0,128160,375,800,1200,0,0,0,0,0,0 +46,Weapon,3,4,20,256320,1875,800,1200,2500,3500,2500,3500,1200,1800 +47,Weapon,3,5,40,512640,2775,800,1200,0,0,0,0,0,0 +48,Weapon,3,6,80,1025280,3900,800,1200,0,0,0,0,0,0 +49,Weapon,3,7,160,2050560,7500,800,1200,2500,3500,2500,3500,1200,1800 +50,Weapon,3,8,320,4101120,8925,800,1200,600,1000,600,1000,400,600 +51,Weapon,3,9,640,8202240,10500,800,1200,600,1000,600,1000,400,600 +52,Weapon,3,10,1280,16404480,14250,800,1200,1100,1700,1100,1700,700,1100 +53,Weapon,3,11,2560,32808960,15000,800,1200,600,1000,600,1000,400,600 +54,Weapon,3,12,5120,65617920,15750,800,1200,600,1000,600,1000,400,600 +55,Weapon,3,13,10240,131235840,16500,800,1200,1100,1700,1100,1700,700,1100 +56,Weapon,3,14,20480,262471680,17250,800,1200,600,1000,600,1000,400,600 +57,Weapon,3,15,40960,524943360,18000,800,1200,600,1000,600,1000,400,600 +58,Weapon,3,16,81920,1049886720,18750,800,1200,1100,1700,1100,1700,700,1100 +59,Weapon,3,17,163840,2099773440,19500,800,1200,600,1000,600,1000,400,600 +60,Weapon,3,18,327680,4199546880,20250,800,1200,600,1000,600,1000,400,600 +61,Weapon,3,19,655360,8399093760,21000,800,1200,1100,1700,1100,1700,700,1100 +62,Weapon,3,20,1310720,16798187520,21750,800,1200,800,1200,800,1200,400,600 +63,Weapon,3,21,2621440,33596375040,22500,800,1200,800,1200,800,1200,400,600 +64,Weapon,4,1,0,852000,162,800,1200,0,0,0,0,0,0 +65,Weapon,4,2,0,1704000,406,800,1200,0,0,0,0,0,0 +66,Weapon,4,3,0,3408000,812,800,1200,0,0,0,0,0,0 +67,Weapon,4,4,20,6816000,4062,800,1200,2500,3500,2500,3500,1200,1800 +68,Weapon,4,5,40,13632000,6012,800,1200,0,0,0,0,0,0 +69,Weapon,4,6,80,27264000,8450,800,1200,0,0,0,0,0,0 +70,Weapon,4,7,160,54528000,16250,800,1200,2500,3500,2500,3500,1200,1800 +71,Weapon,4,8,320,109056000,19337,800,1200,600,1000,600,1000,400,600 +72,Weapon,4,9,640,218112000,22750,800,1200,600,1000,600,1000,400,600 +73,Weapon,4,10,1280,436224000,30875,800,1200,1100,1700,1100,1700,700,1100 +74,Weapon,4,11,2560,872448000,32500,800,1200,600,1000,600,1000,400,600 +75,Weapon,4,12,5120,1744896000,34125,800,1200,600,1000,600,1000,400,600 +76,Weapon,4,13,10240,3489792000,35750,800,1200,1100,1700,1100,1700,700,1100 +77,Weapon,4,14,20480,6979584000,37375,800,1200,600,1000,600,1000,400,600 +78,Weapon,4,15,40960,13959168000,39000,800,1200,600,1000,600,1000,400,600 +79,Weapon,4,16,81920,27918336000,40625,800,1200,1100,1700,1100,1700,700,1100 +80,Weapon,4,17,163840,55836672000,42250,800,1200,600,1000,600,1000,400,600 +81,Weapon,4,18,327680,111673344000,43875,800,1200,600,1000,600,1000,400,600 +82,Weapon,4,19,655360,223346688000,45500,800,1200,1100,1700,1100,1700,700,1100 +83,Weapon,4,20,1310720,446693376000,47125,800,1200,800,1200,800,1200,400,600 +84,Weapon,4,21,2621440,893386752000,48750,800,1200,800,1200,800,1200,400,600 +85,Weapon,5,1,0,13520000,3600,1300,1400,1200,1300,0,0,0,0 +86,Weapon,5,2,0,27040000,3600,1300,1400,1200,1300,0,0,0,0 +87,Weapon,5,3,0,54080000,3600,1300,1400,1200,1300,0,0,0,0 +88,Weapon,5,4,20,108160000,3600,3500,3600,3500,3600,2000,2000,1500,1500 +89,Weapon,5,5,40,216320000,3600,1300,1400,1200,1300,0,0,0,0 +90,Weapon,5,6,80,432640000,3600,1300,1400,1200,1300,2000,2000,1500,1500 +91,Weapon,5,7,160,865280000,3600,1300,1400,1200,1300,0,0,0,0 +92,Weapon,5,8,320,1730560000,3600,1300,1400,1200,1300,0,0,0,0 +93,Weapon,5,9,640,3461120000,3600,1300,1400,1200,1300,2000,2000,1500,1500 +94,Weapon,5,10,1280,6922240000,3600,1300,1400,1200,1300,0,0,0,0 +95,Weapon,5,11,2560,13844480000,3600,1300,1400,1200,1300,0,0,0,0 +96,Weapon,5,12,5120,27688960000,3600,1300,1400,1200,1300,0,0,0,0 +97,Weapon,5,13,10240,55377920000,3600,1300,1400,1200,1300,0,0,0,0 +98,Weapon,5,14,20480,110755840000,3600,1300,1400,1200,1300,0,0,0,0 +99,Weapon,5,15,40960,221511680000,3600,1300,1400,1200,1300,0,0,0,0 +100,Weapon,5,16,81920,443023360000,3600,1300,1400,1200,1300,0,0,0,0 +101,Weapon,5,17,163840,886046720000,3600,1300,1400,1200,1300,0,0,0,0 +102,Weapon,5,18,327680,1772093440000,3600,1300,1400,1200,1300,0,0,0,0 +103,Weapon,5,19,655360,3544186880000,3600,1300,1400,1200,1300,0,0,0,0 +104,Weapon,5,20,1310720,7088373760000,3600,1300,1400,1200,1300,0,0,0,0 +105,Weapon,5,21,2621440,14176747520000,3600,1300,1400,1200,1300,0,0,0,0 +106,Armor,1,1,0,38,20,800,1200,0,0,0,0,0,0 +107,Armor,1,2,0,76,50,800,1200,0,0,0,0,0,0 +108,Armor,1,3,0,152,100,800,1200,0,0,0,0,0,0 +109,Armor,1,4,20,304,500,800,1200,2500,3500,2500,3500,1200,1800 +110,Armor,1,5,40,608,740,800,1200,0,0,0,0,0,0 +111,Armor,1,6,80,1216,1040,800,1200,0,0,0,0,0,0 +112,Armor,1,7,160,2432,2000,800,1200,2500,3500,2500,3500,1200,1800 +113,Armor,1,8,320,4864,2380,800,1200,600,1000,600,1000,400,600 +114,Armor,1,9,640,9728,2800,800,1200,600,1000,600,1000,400,600 +115,Armor,1,10,1280,19456,3800,800,1200,1100,1700,1100,1700,700,1100 +116,Armor,1,11,2560,38912,4000,800,1200,600,1000,600,1000,400,600 +117,Armor,1,12,5120,77824,4200,800,1200,600,1000,600,1000,400,600 +118,Armor,1,13,10240,155648,4400,800,1200,1100,1700,1100,1700,700,1100 +119,Armor,1,14,20480,311296,4600,800,1200,600,1000,600,1000,400,600 +120,Armor,1,15,40960,622592,4800,800,1200,600,1000,600,1000,400,600 +121,Armor,1,16,81920,1245184,5000,800,1200,1100,1700,1100,1700,700,1100 +122,Armor,1,17,163840,2490368,5200,800,1200,600,1000,600,1000,400,600 +123,Armor,1,18,327680,4980736,5400,800,1200,600,1000,600,1000,400,600 +124,Armor,1,19,655360,9961472,5600,800,1200,1100,1700,1100,1700,700,1100 +125,Armor,1,20,1310720,19922944,5800,800,1200,800,1200,800,1200,400,600 +126,Armor,1,21,2621440,39845888,6000,800,1200,800,1200,800,1200,400,600 +127,Armor,2,1,0,206,30,800,1200,0,0,0,0,0,0 +128,Armor,2,2,0,412,75,800,1200,0,0,0,0,0,0 +129,Armor,2,3,0,824,150,800,1200,0,0,0,0,0,0 +130,Armor,2,4,20,1648,750,800,1200,2500,3500,2500,3500,1200,1800 +131,Armor,2,5,40,3296,1110,800,1200,0,0,0,0,0,0 +132,Armor,2,6,80,6592,1560,800,1200,0,0,0,0,0,0 +133,Armor,2,7,160,13184,3000,800,1200,2500,3500,2500,3500,1200,1800 +134,Armor,2,8,320,26368,3570,800,1200,600,1000,600,1000,400,600 +135,Armor,2,9,640,52736,4200,800,1200,600,1000,600,1000,400,600 +136,Armor,2,10,1280,105472,5700,800,1200,1100,1700,1100,1700,700,1100 +137,Armor,2,11,2560,210944,6000,800,1200,600,1000,600,1000,400,600 +138,Armor,2,12,5120,421888,6300,800,1200,600,1000,600,1000,400,600 +139,Armor,2,13,10240,843776,6600,800,1200,1100,1700,1100,1700,700,1100 +140,Armor,2,14,20480,1687552,6900,800,1200,600,1000,600,1000,400,600 +141,Armor,2,15,40960,3375104,7200,800,1200,600,1000,600,1000,400,600 +142,Armor,2,16,81920,6750208,7500,800,1200,1100,1700,1100,1700,700,1100 +143,Armor,2,17,163840,13500416,7800,800,1200,600,1000,600,1000,400,600 +144,Armor,2,18,327680,27000832,8100,800,1200,600,1000,600,1000,400,600 +145,Armor,2,19,655360,54001664,8400,800,1200,1100,1700,1100,1700,700,1100 +146,Armor,2,20,1310720,108003328,8700,800,1200,800,1200,800,1200,400,600 +147,Armor,2,21,2621440,216006656,9000,800,1200,800,1200,800,1200,400,600 +148,Armor,3,1,0,19200,60,800,1200,0,0,0,0,0,0 +149,Armor,3,2,0,38400,150,800,1200,0,0,0,0,0,0 +150,Armor,3,3,0,76800,300,800,1200,0,0,0,0,0,0 +151,Armor,3,4,20,153600,1500,800,1200,2500,3500,2500,3500,1200,1800 +152,Armor,3,5,40,307200,2220,800,1200,0,0,0,0,0,0 +153,Armor,3,6,80,614400,3120,800,1200,0,0,0,0,0,0 +154,Armor,3,7,160,1228800,6000,800,1200,2500,3500,2500,3500,1200,1800 +155,Armor,3,8,320,2457600,7140,800,1200,600,1000,600,1000,400,600 +156,Armor,3,9,640,4915200,8400,800,1200,600,1000,600,1000,400,600 +157,Armor,3,10,1280,9830400,11400,800,1200,1100,1700,1100,1700,700,1100 +158,Armor,3,11,2560,19660800,12000,800,1200,600,1000,600,1000,400,600 +159,Armor,3,12,5120,39321600,12600,800,1200,600,1000,600,1000,400,600 +160,Armor,3,13,10240,78643200,13200,800,1200,1100,1700,1100,1700,700,1100 +161,Armor,3,14,20480,157286400,13800,800,1200,600,1000,600,1000,400,600 +162,Armor,3,15,40960,314572800,14400,800,1200,600,1000,600,1000,400,600 +163,Armor,3,16,81920,629145600,15000,800,1200,1100,1700,1100,1700,700,1100 +164,Armor,3,17,163840,1258291200,15600,800,1200,600,1000,600,1000,400,600 +165,Armor,3,18,327680,2516582400,16200,800,1200,600,1000,600,1000,400,600 +166,Armor,3,19,655360,5033164800,16800,800,1200,1100,1700,1100,1700,700,1100 +167,Armor,3,20,1310720,10066329600,17400,800,1200,800,1200,800,1200,400,600 +168,Armor,3,21,2621440,20132659200,18000,800,1200,800,1200,800,1200,400,600 +169,Armor,4,1,0,852000,130,800,1200,0,0,0,0,0,0 +170,Armor,4,2,0,1704000,325,800,1200,0,0,0,0,0,0 +171,Armor,4,3,0,3408000,650,800,1200,0,0,0,0,0,0 +172,Armor,4,4,20,6816000,3250,800,1200,2500,3500,2500,3500,1200,1800 +173,Armor,4,5,40,13632000,4810,800,1200,0,0,0,0,0,0 +174,Armor,4,6,80,27264000,6760,800,1200,0,0,0,0,0,0 +175,Armor,4,7,160,54528000,13000,800,1200,2500,3500,2500,3500,1200,1800 +176,Armor,4,8,320,109056000,15470,800,1200,600,1000,600,1000,400,600 +177,Armor,4,9,640,218112000,18200,800,1200,600,1000,600,1000,400,600 +178,Armor,4,10,1280,436224000,24700,800,1200,1100,1700,1100,1700,700,1100 +179,Armor,4,11,2560,872448000,26000,800,1200,600,1000,600,1000,400,600 +180,Armor,4,12,5120,1744896000,27300,800,1200,600,1000,600,1000,400,600 +181,Armor,4,13,10240,3489792000,28600,800,1200,1100,1700,1100,1700,700,1100 +182,Armor,4,14,20480,6979584000,29900,800,1200,600,1000,600,1000,400,600 +183,Armor,4,15,40960,13959168000,31200,800,1200,600,1000,600,1000,400,600 +184,Armor,4,16,81920,27918336000,32500,800,1200,1100,1700,1100,1700,700,1100 +185,Armor,4,17,163840,55836672000,33800,800,1200,600,1000,600,1000,400,600 +186,Armor,4,18,327680,111673344000,35100,800,1200,600,1000,600,1000,400,600 +187,Armor,4,19,655360,223346688000,36400,800,1200,1100,1700,1100,1700,700,1100 +188,Armor,4,20,1310720,446693376000,37700,800,1200,800,1200,800,1200,400,600 +189,Armor,4,21,2621440,893386752000,39000,800,1200,800,1200,800,1200,400,600 +190,Armor,5,1,0,13520000,3600,1300,1400,1200,1300,0,0,0,0 +191,Armor,5,2,0,27040000,3600,1300,1400,1200,1300,0,0,0,0 +192,Armor,5,3,0,54080000,3600,1300,1400,1200,1300,0,0,0,0 +193,Armor,5,4,20,108160000,3600,3500,3600,3500,3600,2000,2000,1500,1500 +194,Armor,5,5,40,216320000,3600,1300,1400,1200,1300,0,0,0,0 +195,Armor,5,6,80,432640000,3600,1300,1400,1200,1300,2000,2000,1500,1500 +196,Armor,5,7,160,865280000,3600,1300,1400,1200,1300,0,0,0,0 +197,Armor,5,8,320,1730560000,3600,1300,1400,1200,1300,0,0,0,0 +198,Armor,5,9,640,3461120000,3600,1300,1400,1200,1300,2000,2000,1500,1500 +199,Armor,5,10,1280,6922240000,3600,1300,1400,1200,1300,0,0,0,0 +200,Armor,5,11,2560,13844480000,3600,1300,1400,1200,1300,0,0,0,0 +201,Armor,5,12,5120,27688960000,3600,1300,1400,1200,1300,0,0,0,0 +202,Armor,5,13,10240,55377920000,3600,1300,1400,1200,1300,0,0,0,0 +203,Armor,5,14,20480,110755840000,3600,1300,1400,1200,1300,0,0,0,0 +204,Armor,5,15,40960,221511680000,3600,1300,1400,1200,1300,0,0,0,0 +205,Armor,5,16,81920,443023360000,3600,1300,1400,1200,1300,0,0,0,0 +206,Armor,5,17,163840,886046720000,3600,1300,1400,1200,1300,0,0,0,0 +207,Armor,5,18,327680,1772093440000,3600,1300,1400,1200,1300,0,0,0,0 +208,Armor,5,19,655360,3544186880000,3600,1300,1400,1200,1300,0,0,0,0 +209,Armor,5,20,1310720,7088373760000,3600,1300,1400,1200,1300,0,0,0,0 +210,Armor,5,21,2621440,14176747520000,3600,1300,1400,1200,1300,0,0,0,0 +211,Belt,1,1,0,1908,20,800,1200,0,0,0,0,0,0 +212,Belt,1,2,0,3816,50,800,1200,0,0,0,0,0,0 +213,Belt,1,3,0,7632,100,800,1200,0,0,0,0,0,0 +214,Belt,1,4,20,15264,500,800,1200,2500,3500,2500,3500,1200,1800 +215,Belt,1,5,40,30528,740,800,1200,0,0,0,0,0,0 +216,Belt,1,6,80,61056,1040,800,1200,0,0,0,0,0,0 +217,Belt,1,7,160,122112,2000,800,1200,2500,3500,2500,3500,1200,1800 +218,Belt,1,8,320,244224,2380,800,1200,600,1000,600,1000,400,600 +219,Belt,1,9,640,488448,2800,800,1200,600,1000,600,1000,400,600 +220,Belt,1,10,1280,976896,3800,800,1200,1100,1700,1100,1700,700,1100 +221,Belt,1,11,2560,1953792,4000,800,1200,600,1000,600,1000,400,600 +222,Belt,1,12,5120,3907584,4200,800,1200,600,1000,600,1000,400,600 +223,Belt,1,13,10240,7815168,4400,800,1200,1100,1700,1100,1700,700,1100 +224,Belt,1,14,20480,15630336,4600,800,1200,600,1000,600,1000,400,600 +225,Belt,1,15,40960,31260672,4800,800,1200,600,1000,600,1000,400,600 +226,Belt,1,16,81920,62521344,5000,800,1200,1100,1700,1100,1700,700,1100 +227,Belt,1,17,163840,125042688,5200,800,1200,600,1000,600,1000,400,600 +228,Belt,1,18,327680,250085376,5400,800,1200,600,1000,600,1000,400,600 +229,Belt,1,19,655360,500170752,5600,800,1200,1100,1700,1100,1700,700,1100 +230,Belt,1,20,1310720,1000341504,5800,800,1200,800,1200,800,1200,400,600 +231,Belt,1,21,2621440,2000683008,6000,800,1200,800,1200,800,1200,400,600 +232,Belt,2,1,0,2992,30,800,1200,0,0,0,0,0,0 +233,Belt,2,2,0,5984,75,800,1200,0,0,0,0,0,0 +234,Belt,2,3,0,11968,150,800,1200,0,0,0,0,0,0 +235,Belt,2,4,20,23936,750,800,1200,2500,3500,2500,3500,1200,1800 +236,Belt,2,5,40,47872,1110,800,1200,0,0,0,0,0,0 +237,Belt,2,6,80,95744,1560,800,1200,0,0,0,0,0,0 +238,Belt,2,7,160,191488,3000,800,1200,2500,3500,2500,3500,1200,1800 +239,Belt,2,8,320,382976,3570,800,1200,600,1000,600,1000,400,600 +240,Belt,2,9,640,765952,4200,800,1200,600,1000,600,1000,400,600 +241,Belt,2,10,1280,1531904,5700,800,1200,1100,1700,1100,1700,700,1100 +242,Belt,2,11,2560,3063808,6000,800,1200,600,1000,600,1000,400,600 +243,Belt,2,12,5120,6127616,6300,800,1200,600,1000,600,1000,400,600 +244,Belt,2,13,10240,12255232,6600,800,1200,1100,1700,1100,1700,700,1100 +245,Belt,2,14,20480,24510464,6900,800,1200,600,1000,600,1000,400,600 +246,Belt,2,15,40960,49020928,7200,800,1200,600,1000,600,1000,400,600 +247,Belt,2,16,81920,98041856,7500,800,1200,1100,1700,1100,1700,700,1100 +248,Belt,2,17,163840,196083712,7800,800,1200,600,1000,600,1000,400,600 +249,Belt,2,18,327680,392167424,8100,800,1200,600,1000,600,1000,400,600 +250,Belt,2,19,655360,784334848,8400,800,1200,1100,1700,1100,1700,700,1100 +251,Belt,2,20,1310720,1568669696,8700,800,1200,800,1200,800,1200,400,600 +252,Belt,2,21,2621440,3137339392,9000,800,1200,800,1200,800,1200,400,600 +253,Belt,3,1,0,7728,60,800,1200,0,0,0,0,0,0 +254,Belt,3,2,0,15456,150,800,1200,0,0,0,0,0,0 +255,Belt,3,3,0,30912,300,800,1200,0,0,0,0,0,0 +256,Belt,3,4,20,61824,1500,800,1200,2500,3500,2500,3500,1200,1800 +257,Belt,3,5,40,123648,2220,800,1200,0,0,0,0,0,0 +258,Belt,3,6,80,247296,3120,800,1200,0,0,0,0,0,0 +259,Belt,3,7,160,494592,6000,800,1200,2500,3500,2500,3500,1200,1800 +260,Belt,3,8,320,989184,7140,800,1200,600,1000,600,1000,400,600 +261,Belt,3,9,640,1978368,8400,800,1200,600,1000,600,1000,400,600 +262,Belt,3,10,1280,3956736,11400,800,1200,1100,1700,1100,1700,700,1100 +263,Belt,3,11,2560,7913472,12000,800,1200,600,1000,600,1000,400,600 +264,Belt,3,12,5120,15826944,12600,800,1200,600,1000,600,1000,400,600 +265,Belt,3,13,10240,31653888,13200,800,1200,1100,1700,1100,1700,700,1100 +266,Belt,3,14,20480,63307776,13800,800,1200,600,1000,600,1000,400,600 +267,Belt,3,15,40960,126615552,14400,800,1200,600,1000,600,1000,400,600 +268,Belt,3,16,81920,253231104,15000,800,1200,1100,1700,1100,1700,700,1100 +269,Belt,3,17,163840,506462208,15600,800,1200,600,1000,600,1000,400,600 +270,Belt,3,18,327680,1012924416,16200,800,1200,600,1000,600,1000,400,600 +271,Belt,3,19,655360,2025848832,16800,800,1200,1100,1700,1100,1700,700,1100 +272,Belt,3,20,1310720,4051697664,17400,800,1200,800,1200,800,1200,400,600 +273,Belt,3,21,2621440,8103395328,18000,800,1200,800,1200,800,1200,400,600 +274,Belt,4,1,0,193200,130,800,1200,0,0,0,0,0,0 +275,Belt,4,2,0,386400,325,800,1200,0,0,0,0,0,0 +276,Belt,4,3,0,772800,650,800,1200,0,0,0,0,0,0 +277,Belt,4,4,20,1545600,3250,800,1200,2500,3500,2500,3500,1200,1800 +278,Belt,4,5,40,3091200,4810,800,1200,0,0,0,0,0,0 +279,Belt,4,6,80,6182400,6760,800,1200,0,0,0,0,0,0 +280,Belt,4,7,160,12364800,13000,800,1200,2500,3500,2500,3500,1200,1800 +281,Belt,4,8,320,24729600,15470,800,1200,600,1000,600,1000,400,600 +282,Belt,4,9,640,49459200,18200,800,1200,600,1000,600,1000,400,600 +283,Belt,4,10,1280,98918400,24700,800,1200,1100,1700,1100,1700,700,1100 +284,Belt,4,11,2560,197836800,26000,800,1200,600,1000,600,1000,400,600 +285,Belt,4,12,5120,395673600,27300,800,1200,600,1000,600,1000,400,600 +286,Belt,4,13,10240,791347200,28600,800,1200,1100,1700,1100,1700,700,1100 +287,Belt,4,14,20480,1582694400,29900,800,1200,600,1000,600,1000,400,600 +288,Belt,4,15,40960,3165388800,31200,800,1200,600,1000,600,1000,400,600 +289,Belt,4,16,81920,6330777600,32500,800,1200,1100,1700,1100,1700,700,1100 +290,Belt,4,17,163840,12661555200,33800,800,1200,600,1000,600,1000,400,600 +291,Belt,4,18,327680,25323110400,35100,800,1200,600,1000,600,1000,400,600 +292,Belt,4,19,655360,50646220800,36400,800,1200,1100,1700,1100,1700,700,1100 +293,Belt,4,20,1310720,101292441600,37700,800,1200,800,1200,800,1200,400,600 +294,Belt,4,21,2621440,202584883200,39000,800,1200,800,1200,800,1200,400,600 +295,Belt,5,1,0,13520000,3600,1300,1400,1200,1300,0,0,0,0 +296,Belt,5,2,0,27040000,3600,1300,1400,1200,1300,0,0,0,0 +297,Belt,5,3,0,54080000,3600,1300,1400,1200,1300,0,0,0,0 +298,Belt,5,4,20,108160000,3600,3500,3600,3500,3600,2000,2000,1500,1500 +299,Belt,5,5,40,216320000,3600,1300,1400,1200,1300,0,0,0,0 +300,Belt,5,6,80,432640000,3600,1300,1400,1200,1300,2000,2000,1500,1500 +301,Belt,5,7,160,865280000,3600,1300,1400,1200,1300,0,0,0,0 +302,Belt,5,8,320,1730560000,3600,1300,1400,1200,1300,0,0,0,0 +303,Belt,5,9,640,3461120000,3600,1300,1400,1200,1300,2000,2000,1500,1500 +304,Belt,5,10,1280,6922240000,3600,1300,1400,1200,1300,0,0,0,0 +305,Belt,5,11,2560,13844480000,3600,1300,1400,1200,1300,0,0,0,0 +306,Belt,5,12,5120,27688960000,3600,1300,1400,1200,1300,0,0,0,0 +307,Belt,5,13,10240,55377920000,3600,1300,1400,1200,1300,0,0,0,0 +308,Belt,5,14,20480,110755840000,3600,1300,1400,1200,1300,0,0,0,0 +309,Belt,5,15,40960,221511680000,3600,1300,1400,1200,1300,0,0,0,0 +310,Belt,5,16,81920,443023360000,3600,1300,1400,1200,1300,0,0,0,0 +311,Belt,5,17,163840,886046720000,3600,1300,1400,1200,1300,0,0,0,0 +312,Belt,5,18,327680,1772093440000,3600,1300,1400,1200,1300,0,0,0,0 +313,Belt,5,19,655360,3544186880000,3600,1300,1400,1200,1300,0,0,0,0 +314,Belt,5,20,1310720,7088373760000,3600,1300,1400,1200,1300,0,0,0,0 +315,Belt,5,21,2621440,14176747520000,3600,1300,1400,1200,1300,0,0,0,0 +316,Necklace,1,1,0,630,25,800,1200,0,0,0,0,0,0 +317,Necklace,1,2,0,1260,62,800,1200,0,0,0,0,0,0 +318,Necklace,1,3,0,2520,125,800,1200,0,0,0,0,0,0 +319,Necklace,1,4,20,5040,625,800,1200,2500,3500,2500,3500,1200,1800 +320,Necklace,1,5,40,10080,925,800,1200,0,0,0,0,0,0 +321,Necklace,1,6,80,20160,1300,800,1200,0,0,0,0,0,0 +322,Necklace,1,7,160,40320,2500,800,1200,2500,3500,2500,3500,1200,1800 +323,Necklace,1,8,320,80640,2975,800,1200,600,1000,600,1000,400,600 +324,Necklace,1,9,640,161280,3500,800,1200,600,1000,600,1000,400,600 +325,Necklace,1,10,1280,322560,4750,800,1200,1100,1700,1100,1700,700,1100 +326,Necklace,1,11,2560,645120,5000,800,1200,600,1000,600,1000,400,600 +327,Necklace,1,12,5120,1290240,5250,800,1200,600,1000,600,1000,400,600 +328,Necklace,1,13,10240,2580480,5500,800,1200,1100,1700,1100,1700,700,1100 +329,Necklace,1,14,20480,5160960,5750,800,1200,600,1000,600,1000,400,600 +330,Necklace,1,15,40960,10321920,6000,800,1200,600,1000,600,1000,400,600 +331,Necklace,1,16,81920,20643840,6250,800,1200,1100,1700,1100,1700,700,1100 +332,Necklace,1,17,163840,41287680,6500,800,1200,600,1000,600,1000,400,600 +333,Necklace,1,18,327680,82575360,6750,800,1200,600,1000,600,1000,400,600 +334,Necklace,1,19,655360,165150720,7000,800,1200,1100,1700,1100,1700,700,1100 +335,Necklace,1,20,1310720,330301440,7250,800,1200,800,1200,800,1200,400,600 +336,Necklace,1,21,2621440,660602880,7500,800,1200,800,1200,800,1200,400,600 +337,Necklace,2,1,0,3472,37,800,1200,0,0,0,0,0,0 +338,Necklace,2,2,0,6944,93,800,1200,0,0,0,0,0,0 +339,Necklace,2,3,0,13888,187,800,1200,0,0,0,0,0,0 +340,Necklace,2,4,20,27776,937,800,1200,2500,3500,2500,3500,1200,1800 +341,Necklace,2,5,40,55552,1387,800,1200,0,0,0,0,0,0 +342,Necklace,2,6,80,111104,1950,800,1200,0,0,0,0,0,0 +343,Necklace,2,7,160,222208,3750,800,1200,2500,3500,2500,3500,1200,1800 +344,Necklace,2,8,320,444416,4462,800,1200,600,1000,600,1000,400,600 +345,Necklace,2,9,640,888832,5250,800,1200,600,1000,600,1000,400,600 +346,Necklace,2,10,1280,1777664,7125,800,1200,1100,1700,1100,1700,700,1100 +347,Necklace,2,11,2560,3555328,7500,800,1200,600,1000,600,1000,400,600 +348,Necklace,2,12,5120,7110656,7875,800,1200,600,1000,600,1000,400,600 +349,Necklace,2,13,10240,14221312,8250,800,1200,1100,1700,1100,1700,700,1100 +350,Necklace,2,14,20480,28442624,8625,800,1200,600,1000,600,1000,400,600 +351,Necklace,2,15,40960,56885248,9000,800,1200,600,1000,600,1000,400,600 +352,Necklace,2,16,81920,113770496,9375,800,1200,1100,1700,1100,1700,700,1100 +353,Necklace,2,17,163840,227540992,9750,800,1200,600,1000,600,1000,400,600 +354,Necklace,2,18,327680,455081984,10125,800,1200,600,1000,600,1000,400,600 +355,Necklace,2,19,655360,910163968,10500,800,1200,1100,1700,1100,1700,700,1100 +356,Necklace,2,20,1310720,1820327936,10875,800,1200,800,1200,800,1200,400,600 +357,Necklace,2,21,2621440,3640655872,11250,800,1200,800,1200,800,1200,400,600 +358,Necklace,3,1,0,130080,75,800,1200,0,0,0,0,0,0 +359,Necklace,3,2,0,260160,187,800,1200,0,0,0,0,0,0 +360,Necklace,3,3,0,520320,375,800,1200,0,0,0,0,0,0 +361,Necklace,3,4,20,1040640,1875,800,1200,2500,3500,2500,3500,1200,1800 +362,Necklace,3,5,40,2081280,2775,800,1200,0,0,0,0,0,0 +363,Necklace,3,6,80,4162560,3900,800,1200,0,0,0,0,0,0 +364,Necklace,3,7,160,8325120,7500,800,1200,2500,3500,2500,3500,1200,1800 +365,Necklace,3,8,320,16650240,8925,800,1200,600,1000,600,1000,400,600 +366,Necklace,3,9,640,33300480,10500,800,1200,600,1000,600,1000,400,600 +367,Necklace,3,10,1280,66600960,14250,800,1200,1100,1700,1100,1700,700,1100 +368,Necklace,3,11,2560,133201920,15000,800,1200,600,1000,600,1000,400,600 +369,Necklace,3,12,5120,266403840,15750,800,1200,600,1000,600,1000,400,600 +370,Necklace,3,13,10240,532807680,16500,800,1200,1100,1700,1100,1700,700,1100 +371,Necklace,3,14,20480,1065615360,17250,800,1200,600,1000,600,1000,400,600 +372,Necklace,3,15,40960,2131230720,18000,800,1200,600,1000,600,1000,400,600 +373,Necklace,3,16,81920,4262461440,18750,800,1200,1100,1700,1100,1700,700,1100 +374,Necklace,3,17,163840,8524922880,19500,800,1200,600,1000,600,1000,400,600 +375,Necklace,3,18,327680,17049845760,20250,800,1200,600,1000,600,1000,400,600 +376,Necklace,3,19,655360,34099691520,21000,800,1200,1100,1700,1100,1700,700,1100 +377,Necklace,3,20,1310720,68199383040,21750,800,1200,800,1200,800,1200,400,600 +378,Necklace,3,21,2621440,136398766080,22500,800,1200,800,1200,800,1200,400,600 +379,Necklace,4,1,0,486000,162,800,1200,0,0,0,0,0,0 +380,Necklace,4,2,0,972000,406,800,1200,0,0,0,0,0,0 +381,Necklace,4,3,0,1944000,812,800,1200,0,0,0,0,0,0 +382,Necklace,4,4,20,3888000,4062,800,1200,2500,3500,2500,3500,1200,1800 +383,Necklace,4,5,40,7776000,6012,800,1200,0,0,0,0,0,0 +384,Necklace,4,6,80,15552000,8450,800,1200,0,0,0,0,0,0 +385,Necklace,4,7,160,31104000,16250,800,1200,2500,3500,2500,3500,1200,1800 +386,Necklace,4,8,320,62208000,19337,800,1200,600,1000,600,1000,400,600 +387,Necklace,4,9,640,124416000,22750,800,1200,600,1000,600,1000,400,600 +388,Necklace,4,10,1280,248832000,30875,800,1200,1100,1700,1100,1700,700,1100 +389,Necklace,4,11,2560,497664000,32500,800,1200,600,1000,600,1000,400,600 +390,Necklace,4,12,5120,995328000,34125,800,1200,600,1000,600,1000,400,600 +391,Necklace,4,13,10240,1990656000,35750,800,1200,1100,1700,1100,1700,700,1100 +392,Necklace,4,14,20480,3981312000,37375,800,1200,600,1000,600,1000,400,600 +393,Necklace,4,15,40960,7962624000,39000,800,1200,600,1000,600,1000,400,600 +394,Necklace,4,16,81920,15925248000,40625,800,1200,1100,1700,1100,1700,700,1100 +395,Necklace,4,17,163840,31850496000,42250,800,1200,600,1000,600,1000,400,600 +396,Necklace,4,18,327680,63700992000,43875,800,1200,600,1000,600,1000,400,600 +397,Necklace,4,19,655360,127401984000,45500,800,1200,1100,1700,1100,1700,700,1100 +398,Necklace,4,20,1310720,254803968000,47125,800,1200,800,1200,800,1200,400,600 +399,Necklace,4,21,2621440,509607936000,48750,800,1200,800,1200,800,1200,400,600 +400,Necklace,5,1,0,13520000,3600,1300,1400,1200,1300,0,0,0,0 +401,Necklace,5,2,0,27040000,3600,1300,1400,1200,1300,0,0,0,0 +402,Necklace,5,3,0,54080000,3600,1300,1400,1200,1300,0,0,0,0 +403,Necklace,5,4,20,108160000,3600,3500,3600,3500,3600,2000,2000,1500,1500 +404,Necklace,5,5,40,216320000,3600,1300,1400,1200,1300,0,0,0,0 +405,Necklace,5,6,80,432640000,3600,1300,1400,1200,1300,2000,2000,1500,1500 +406,Necklace,5,7,160,865280000,3600,1300,1400,1200,1300,0,0,0,0 +407,Necklace,5,8,320,1730560000,3600,1300,1400,1200,1300,0,0,0,0 +408,Necklace,5,9,640,3461120000,3600,1300,1400,1200,1300,2000,2000,1500,1500 +409,Necklace,5,10,1280,6922240000,3600,1300,1400,1200,1300,0,0,0,0 +410,Necklace,5,11,2560,13844480000,3600,1300,1400,1200,1300,0,0,0,0 +411,Necklace,5,12,5120,27688960000,3600,1300,1400,1200,1300,0,0,0,0 +412,Necklace,5,13,10240,55377920000,3600,1300,1400,1200,1300,0,0,0,0 +413,Necklace,5,14,20480,110755840000,3600,1300,1400,1200,1300,0,0,0,0 +414,Necklace,5,15,40960,221511680000,3600,1300,1400,1200,1300,0,0,0,0 +415,Necklace,5,16,81920,443023360000,3600,1300,1400,1200,1300,0,0,0,0 +416,Necklace,5,17,163840,886046720000,3600,1300,1400,1200,1300,0,0,0,0 +417,Necklace,5,18,327680,1772093440000,3600,1300,1400,1200,1300,0,0,0,0 +418,Necklace,5,19,655360,3544186880000,3600,1300,1400,1200,1300,0,0,0,0 +419,Necklace,5,20,1310720,7088373760000,3600,1300,1400,1200,1300,0,0,0,0 +420,Necklace,5,21,2621440,14176747520000,3600,1300,1400,1200,1300,0,0,0,0 +421,Ring,1,1,0,20,30,800,1200,0,0,0,0,0,0 +422,Ring,1,2,0,40,75,800,1200,0,0,0,0,0,0 +423,Ring,1,3,0,80,150,800,1200,0,0,0,0,0,0 +424,Ring,1,4,20,160,750,800,1200,2500,3500,2500,3500,1200,1800 +425,Ring,1,5,40,320,1110,800,1200,0,0,0,0,0,0 +426,Ring,1,6,80,640,1560,800,1200,0,0,0,0,0,0 +427,Ring,1,7,160,1280,3000,800,1200,2500,3500,2500,3500,1200,1800 +428,Ring,1,8,320,2560,3570,800,1200,600,1000,600,1000,400,600 +429,Ring,1,9,640,5120,4200,800,1200,600,1000,600,1000,400,600 +430,Ring,1,10,1280,10240,5700,800,1200,1100,1700,1100,1700,700,1100 +431,Ring,1,11,2560,20480,6000,800,1200,600,1000,600,1000,400,600 +432,Ring,1,12,5120,40960,6300,800,1200,600,1000,600,1000,400,600 +433,Ring,1,13,10240,81920,6600,800,1200,1100,1700,1100,1700,700,1100 +434,Ring,1,14,20480,163840,6900,800,1200,600,1000,600,1000,400,600 +435,Ring,1,15,40960,327680,7200,800,1200,600,1000,600,1000,400,600 +436,Ring,1,16,81920,655360,7500,800,1200,1100,1700,1100,1700,700,1100 +437,Ring,1,17,163840,1310720,7800,800,1200,600,1000,600,1000,400,600 +438,Ring,1,18,327680,2621440,8100,800,1200,600,1000,600,1000,400,600 +439,Ring,1,19,655360,5242880,8400,800,1200,1100,1700,1100,1700,700,1100 +440,Ring,1,20,1310720,10485760,8700,800,1200,800,1200,800,1200,400,600 +441,Ring,1,21,2621440,20971520,9000,800,1200,800,1200,800,1200,400,600 +442,Ring,2,1,0,432,45,800,1200,0,0,0,0,0,0 +443,Ring,2,2,0,864,112,800,1200,0,0,0,0,0,0 +444,Ring,2,3,0,1728,225,800,1200,0,0,0,0,0,0 +445,Ring,2,4,20,3456,1125,800,1200,2500,3500,2500,3500,1200,1800 +446,Ring,2,5,40,6912,1665,800,1200,0,0,0,0,0,0 +447,Ring,2,6,80,13824,2340,800,1200,0,0,0,0,0,0 +448,Ring,2,7,160,27648,4500,800,1200,2500,3500,2500,3500,1200,1800 +449,Ring,2,8,320,55296,5355,800,1200,600,1000,600,1000,400,600 +450,Ring,2,9,640,110592,6300,800,1200,600,1000,600,1000,400,600 +451,Ring,2,10,1280,221184,8550,800,1200,1100,1700,1100,1700,700,1100 +452,Ring,2,11,2560,442368,9000,800,1200,600,1000,600,1000,400,600 +453,Ring,2,12,5120,884736,9450,800,1200,600,1000,600,1000,400,600 +454,Ring,2,13,10240,1769472,9900,800,1200,1100,1700,1100,1700,700,1100 +455,Ring,2,14,20480,3538944,10350,800,1200,600,1000,600,1000,400,600 +456,Ring,2,15,40960,7077888,10800,800,1200,600,1000,600,1000,400,600 +457,Ring,2,16,81920,14155776,11250,800,1200,1100,1700,1100,1700,700,1100 +458,Ring,2,17,163840,28311552,11700,800,1200,600,1000,600,1000,400,600 +459,Ring,2,18,327680,56623104,12150,800,1200,600,1000,600,1000,400,600 +460,Ring,2,19,655360,113246208,12600,800,1200,1100,1700,1100,1700,700,1100 +461,Ring,2,20,1310720,226492416,13050,800,1200,800,1200,800,1200,400,600 +462,Ring,2,21,2621440,452984832,13500,800,1200,800,1200,800,1200,400,600 +463,Ring,3,1,0,11136,90,800,1200,0,0,0,0,0,0 +464,Ring,3,2,0,22272,225,800,1200,0,0,0,0,0,0 +465,Ring,3,3,0,44544,450,800,1200,0,0,0,0,0,0 +466,Ring,3,4,20,89088,2250,800,1200,2500,3500,2500,3500,1200,1800 +467,Ring,3,5,40,178176,3330,800,1200,0,0,0,0,0,0 +468,Ring,3,6,80,356352,4680,800,1200,0,0,0,0,0,0 +469,Ring,3,7,160,712704,9000,800,1200,2500,3500,2500,3500,1200,1800 +470,Ring,3,8,320,1425408,10710,800,1200,600,1000,600,1000,400,600 +471,Ring,3,9,640,2850816,12600,800,1200,600,1000,600,1000,400,600 +472,Ring,3,10,1280,5701632,17100,800,1200,1100,1700,1100,1700,700,1100 +473,Ring,3,11,2560,11403264,18000,800,1200,600,1000,600,1000,400,600 +474,Ring,3,12,5120,22806528,18900,800,1200,600,1000,600,1000,400,600 +475,Ring,3,13,10240,45613056,19800,800,1200,1100,1700,1100,1700,700,1100 +476,Ring,3,14,20480,91226112,20700,800,1200,600,1000,600,1000,400,600 +477,Ring,3,15,40960,182452224,21600,800,1200,600,1000,600,1000,400,600 +478,Ring,3,16,81920,364904448,22500,800,1200,1100,1700,1100,1700,700,1100 +479,Ring,3,17,163840,729808896,23400,800,1200,600,1000,600,1000,400,600 +480,Ring,3,18,327680,1459617792,24300,800,1200,600,1000,600,1000,400,600 +481,Ring,3,19,655360,2919235584,25200,800,1200,1100,1700,1100,1700,700,1100 +482,Ring,3,20,1310720,5838471168,26100,800,1200,800,1200,800,1200,400,600 +483,Ring,3,21,2621440,11676942336,27000,800,1200,800,1200,800,1200,400,600 +484,Ring,4,1,0,852000,195,800,1200,0,0,0,0,0,0 +485,Ring,4,2,0,1704000,487,800,1200,0,0,0,0,0,0 +486,Ring,4,3,0,3408000,975,800,1200,0,0,0,0,0,0 +487,Ring,4,4,20,6816000,4875,800,1200,2500,3500,2500,3500,1200,1800 +488,Ring,4,5,40,13632000,7215,800,1200,0,0,0,0,0,0 +489,Ring,4,6,80,27264000,10140,800,1200,0,0,0,0,0,0 +490,Ring,4,7,160,54528000,19500,800,1200,2500,3500,2500,3500,1200,1800 +491,Ring,4,8,320,109056000,23205,800,1200,600,1000,600,1000,400,600 +492,Ring,4,9,640,218112000,27300,800,1200,600,1000,600,1000,400,600 +493,Ring,4,10,1280,436224000,37050,800,1200,1100,1700,1100,1700,700,1100 +494,Ring,4,11,2560,872448000,39000,800,1200,600,1000,600,1000,400,600 +495,Ring,4,12,5120,1744896000,40950,800,1200,600,1000,600,1000,400,600 +496,Ring,4,13,10240,3489792000,42900,800,1200,1100,1700,1100,1700,700,1100 +497,Ring,4,14,20480,6979584000,44850,800,1200,600,1000,600,1000,400,600 +498,Ring,4,15,40960,13959168000,46800,800,1200,600,1000,600,1000,400,600 +499,Ring,4,16,81920,27918336000,48750,800,1200,1100,1700,1100,1700,700,1100 +500,Ring,4,17,163840,55836672000,50700,800,1200,600,1000,600,1000,400,600 +501,Ring,4,18,327680,111673344000,52650,800,1200,600,1000,600,1000,400,600 +502,Ring,4,19,655360,223346688000,54600,800,1200,1100,1700,1100,1700,700,1100 +503,Ring,4,20,1310720,446693376000,56550,800,1200,800,1200,800,1200,400,600 +504,Ring,4,21,2621440,893386752000,58500,800,1200,800,1200,800,1200,400,600 +505,Ring,5,1,0,13520000,3600,1300,1400,1200,1300,0,0,0,0 +506,Ring,5,2,0,27040000,3600,1300,1400,1200,1300,0,0,0,0 +507,Ring,5,3,0,54080000,3600,1300,1400,1200,1300,0,0,0,0 +508,Ring,5,4,20,108160000,3600,3500,3600,3500,3600,2000,2000,1500,1500 +509,Ring,5,5,40,216320000,3600,1300,1400,1200,1300,0,0,0,0 +510,Ring,5,6,80,432640000,3600,1300,1400,1200,1300,2000,2000,1500,1500 +511,Ring,5,7,160,865280000,3600,1300,1400,1200,1300,0,0,0,0 +512,Ring,5,8,320,1730560000,3600,1300,1400,1200,1300,0,0,0,0 +513,Ring,5,9,640,3461120000,3600,1300,1400,1200,1300,2000,2000,1500,1500 +514,Ring,5,10,1280,6922240000,3600,1300,1400,1200,1300,0,0,0,0 +515,Ring,5,11,2560,13844480000,3600,1300,1400,1200,1300,0,0,0,0 +516,Ring,5,12,5120,27688960000,3600,1300,1400,1200,1300,0,0,0,0 +517,Ring,5,13,10240,55377920000,3600,1300,1400,1200,1300,0,0,0,0 +518,Ring,5,14,20480,110755840000,3600,1300,1400,1200,1300,0,0,0,0 +519,Ring,5,15,40960,221511680000,3600,1300,1400,1200,1300,0,0,0,0 +520,Ring,5,16,81920,443023360000,3600,1300,1400,1200,1300,0,0,0,0 +521,Ring,5,17,163840,886046720000,3600,1300,1400,1200,1300,0,0,0,0 +522,Ring,5,18,327680,1772093440000,3600,1300,1400,1200,1300,0,0,0,0 +523,Ring,5,19,655360,3544186880000,3600,1300,1400,1200,1300,0,0,0,0 +524,Ring,5,20,1310720,7088373760000,3600,1300,1400,1200,1300,0,0,0,0 +525,Ring,5,21,2621440,14176747520000,3600,1300,1400,1200,1300,0,0,0,0 diff --git a/Lib9c/TableCSV/Item/EquipmentItemSheet.csv b/Lib9c/TableCSV/Item/EquipmentItemSheet.csv index 18ed960a0a..f31ffe1526 100644 --- a/Lib9c/TableCSV/Item/EquipmentItemSheet.csv +++ b/Lib9c/TableCSV/Item/EquipmentItemSheet.csv @@ -1,177 +1,177 @@ -id,_name,item_sub_type,grade,elemental_type,set_id,stat_type,stat_value,attack_range,spine_resource_path -10100000,나뭇가지,Weapon,0,Normal,0,ATK,1,2,10100000 -10110000,검,Weapon,1,Normal,1,ATK,11,2,10110000 -10111000,롱 소드(불),Weapon,1,Fire,2,ATK,14,2,10111000 -10112000,롱 소드(물),Weapon,1,Water,3,ATK,17,2,10112000 -10113000,롱 소드(땅),Weapon,1,Land,4,ATK,55,2,10113000 -10114000,롱 소드(바람),Weapon,1,Wind,5,ATK,136,2,10114000 -10120000,검투사 검,Weapon,2,Normal,6,ATK,31,2,10120000 -10121000,검투사 검(불),Weapon,2,Fire,7,ATK,32,2,10121000 -10122000,검투사 검(물),Weapon,2,Water,8,ATK,38,2,10122000 -10123000,검투사 검(땅),Weapon,2,Land,9,ATK,152,2,10123000 -10124000,검투사 검(바람),Weapon,2,Wind,10,ATK,327,2,10124000 -10130000,까마귀의 검,Weapon,3,Normal,11,ATK,78,2,10130000 -10131000,까마귀의 검(불),Weapon,3,Fire,12,ATK,81,2,10131000 -10132000,까마귀의 검(물),Weapon,3,Water,13,ATK,92,2,10132000 -10133000,까마귀의 검(땅),Weapon,3,Land,14,ATK,354,2,10133000 -10134000,까마귀의 검(바람),Weapon,3,Wind,15,ATK,693,2,10134000 -10130001,무거운 검,Weapon,3,Normal,11,ATK,170,2,10130001 -10131001,무거운 검(불),Weapon,3,Fire,12,ATK,179,2,10131001 -10132001,무거운 검(물),Weapon,3,Water,13,ATK,196,2,10132001 -10133001,무거운 검(땅),Weapon,3,Land,14,ATK,908,2,10133001 -10134001,무거운 검(바람),Weapon,3,Wind,15,ATK,967,2,10134001 -10140000,전쟁 검,Weapon,4,Normal,11,ATK,3094,2,10140000 -10141000,전쟁 검(불),Weapon,4,Fire,12,ATK,1934,2,10141000 -10142000,전쟁 검(물),Weapon,4,Water,13,ATK,2224,2,10142000 -10143000,전쟁 검(땅),Weapon,4,Land,14,ATK,2514,2,10143000 -10144000,전쟁 검(바람),Weapon,4,Wind,15,ATK,2804,2,10144000 -10150000,다인슬레이프,Weapon,5,Normal,16,ATK,6063,2,10150000 -10151000,다인슬레이프(불),Weapon,5,Fire,17,ATK,4331,2,10151000 -10152000,다인슬레이프(물),Weapon,5,Water,18,ATK,4331,2,10152000 -10153000,다인슬레이프(땅),Weapon,5,Land,19,ATK,6063,2,10153000 -10154000,다인슬레이프(바람),Weapon,5,Wind,20,ATK,6063,2,10154000 -10140001,아스가르드의 검,Weapon,5,Normal,11,ATK,765,2,10140001 -10141001,아스가르드의 검(불),Weapon,5,Fire,12,ATK,782,2,10141001 -10142001,아스가르드의 검(물),Weapon,5,Water,13,ATK,798,2,10142001 -10143001,아스가르드의 검(땅),Weapon,5,Land,14,ATK,1221,2,10143001 -10144001,아스가르드의 검(바람),Weapon,5,Wind,15,ATK,1525,2,10144001 -10150001,수르트의 검,Weapon,5,Normal,16,ATK,2157,2,10150001 -10151001,수르트의 검(불),Weapon,5,Fire,17,ATK,2190,2,10151001 -10152001,수르트의 검(물),Weapon,5,Water,18,ATK,2223,2,10152001 -10153001,수르트의 검(땅),Weapon,5,Land,19,ATK,3390,2,10153001 -10154001,수르트의 검(바람),Weapon,5,Wind,20,ATK,4219,2,10154001 -10155000,발키리의 검,Weapon,5,Normal,15,ATK,2902,2,10155000 -10200000,누더기 옷,Armor,0,Normal,0,HP,30,2,Character/Player/10200000 -10210000,옷,Armor,1,Normal,1,HP,180,2,Character/Player/10210000 -10211000,천 옷(불),Armor,1,Fire,2,HP,222,2,Character/Player/10211000 -10212000,천 옷(물),Armor,1,Water,3,HP,288,2,Character/Player/10212000 -10213000,천 옷(땅),Armor,1,Land,4,HP,915,2,Character/Player/10213000 -10214000,천 옷(바람),Armor,1,Wind,5,HP,2182,2,Character/Player/10214000 -10220000,가죽 옷,Armor,2,Normal,6,HP,531,2,Character/Player/10220000 -10221000,가죽 옷(불),Armor,2,Fire,7,HP,549,2,Character/Player/10221000 -10222000,가죽 옷(물),Armor,2,Water,8,HP,621,2,Character/Player/10222000 -10223000,가죽 옷(땅),Armor,2,Land,9,HP,2431,2,Character/Player/10223000 -10224000,가죽 옷(바람),Armor,2,Wind,10,HP,5109,2,Character/Player/10224000 -10230000,검은 까마귀의 갑옷,Armor,3,Normal,11,HP,1337,2,Character/Player/10230000 -10231000,검은 까마귀의 갑옷(불),Armor,3,Fire,12,HP,1418,2,Character/Player/10231000 -10232000,검은 까마귀의 갑옷(물),Armor,3,Water,13,HP,1580,2,Character/Player/10232000 -10233000,검은 까마귀의 갑옷(땅),Armor,3,Land,14,HP,8370,2,Character/Player/10233000 -10234000,검은 까마귀의 갑옷(바람),Armor,3,Wind,15,HP,10571,2,Character/Player/10234000 -10230001,헤르메스의 옷,Armor,3,Normal,11,HP,2811,2,Character/Player/10230001 -10231001,헤르메스의 옷(불),Armor,3,Fire,12,HP,3009,2,Character/Player/10231001 -10232001,헤르메스의 옷(물),Armor,3,Water,13,HP,3141,2,Character/Player/10232001 -10233001,헤르메스의 옷(땅),Armor,3,Land,14,HP,11482,2,Character/Player/10233001 -10234001,헤르메스의 옷(바람),Armor,3,Wind,15,HP,12302,2,Character/Player/10234001 -10240000,전쟁 갑옷,Armor,4,Normal,11,HP,34446,2,Character/Player/10240000 -10241000,전쟁 갑옷(불),Armor,4,Fire,12,HP,24604,2,Character/Player/10241000 -10242000,전쟁 갑옷(물),Armor,4,Water,13,HP,27064,2,Character/Player/10242000 -10243000,전쟁 갑옷(땅),Armor,4,Land,14,HP,29525,2,Character/Player/10243000 -10244000,전쟁 갑옷(바람),Armor,4,Wind,15,HP,31985,2,Character/Player/10244000 -10250001,고대의 갑옷,Armor,5,Normal,11,HP,67513,2,Character/Player/10250001 -10251001,고대의 갑옷(불),Armor,5,Fire,12,HP,48224,2,Character/Player/10251001 -10252001,고대의 갑옷(물),Armor,5,Water,13,HP,48224,2,Character/Player/10252001 -10253001,고대의 갑옷(땅),Armor,5,Land,14,HP,67513,2,Character/Player/10253001 -10254001,고대의 갑옷(바람),Armor,5,Wind,15,HP,67513,2,Character/Player/10254001 -10250000,전설의 갑옷,Armor,5,Normal,11,HP,15550,2,Character/Player/10250000 -10251000,전설의 갑옷(불),Armor,5,Fire,12,HP,15848,2,Character/Player/10251000 -10252000,전설의 갑옷(물),Armor,5,Water,13,HP,16146,2,Character/Player/10252000 -10253000,전설의 갑옷(땅),Armor,5,Land,14,HP,24665,2,Character/Player/10253000 -10254000,전설의 갑옷(바람),Armor,5,Wind,15,HP,30823,2,Character/Player/10254000 -10240001,아스가르드의 갑옷,Armor,5,Normal,11,HP,10358,2,Character/Player/10240001 -10241001,아스가르드의 갑옷(불),Armor,5,Fire,12,HP,10571,2,Character/Player/10241001 -10242001,아스가르드의 갑옷(물),Armor,5,Water,13,HP,10783,2,Character/Player/10242001 -10243001,아스가르드의 갑옷(땅),Armor,5,Land,14,HP,16494,2,Character/Player/10243001 -10244001,아스가르드의 갑옷(바람),Armor,5,Wind,15,HP,20647,2,Character/Player/10244001 -10255000,천상의 고양이,Armor,5,Normal,11,HP,42856,2,Character/Player/10235000 -10310000,허리끈,Belt,1,Normal,1,SPD,46,2,10310000 -10311000,허리끈(불),Belt,1,Fire,2,SPD,62,2,10311000 -10312000,허리끈(물),Belt,1,Water,3,SPD,69,2,10312000 -10313000,허리끈(땅),Belt,1,Land,4,SPD,239,2,10313000 -10314000,허리끈(바람),Belt,1,Wind,5,SPD,610,2,10314000 -10320000,가죽 벨트,Belt,2,Normal,6,SPD,137,2,10320000 -10321000,가죽 벨트(불),Belt,2,Fire,7,SPD,141,2,10321000 -10322000,가죽 벨트(물),Belt,2,Water,8,SPD,166,2,10322000 -10323000,가죽 벨트(땅),Belt,2,Land,9,SPD,718,2,10323000 -10324000,가죽 벨트(바람),Belt,2,Wind,10,SPD,1314,2,10324000 -10330000,견고한 벨트,Belt,3,Normal,11,SPD,350,2,10330000 -10331000,견고한 벨트(불),Belt,3,Fire,12,SPD,359,2,10331000 -10332000,견고한 벨트(물),Belt,3,Water,13,SPD,425,2,10332000 -10333000,견고한 벨트(땅),Belt,3,Land,14,SPD,1701,2,10333000 -10334000,견고한 벨트(바람),Belt,3,Wind,15,SPD,2883,2,10334000 -10340000,전쟁 벨트,Belt,4,Normal,11,SPD,1215,2,10340000 -10341000,전쟁 벨트(불),Belt,4,Fire,12,SPD,1262,2,10341000 -10342000,전쟁 벨트(물),Belt,4,Water,13,SPD,2292,2,10342000 -10343000,전쟁 벨트(땅),Belt,4,Land,14,SPD,2292,2,10343000 -10344000,전쟁 벨트(바람),Belt,4,Wind,15,SPD,2292,2,10344000 -10350000,전설의 벨트,Belt,4,Normal,11,SPD,7334,2,10350000 -10351000,전설의 벨트(불),Belt,4,Fire,12,SPD,4584,2,10351000 -10352000,전설의 벨트(물),Belt,4,Water,13,SPD,5272,2,10352000 -10353000,전설의 벨트(땅),Belt,4,Land,14,SPD,5959,2,10353000 -10354000,전설의 벨트(바람),Belt,4,Wind,15,SPD,6647,2,10354000 -10350001,고대의 벨트,Belt,5,Normal,15,SPD,14373,2,10350000 -10351001,고대의 벨트(불),Belt,5,Fire,15,SPD,10267,2,10351000 -10352001,고대의 벨트(물),Belt,5,Water,15,SPD,10267,2,10352000 -10353001,고대의 벨트(땅),Belt,5,Land,15,SPD,14373,2,10353000 -10354001,고대의 벨트(바람),Belt,5,Wind,15,SPD,14373,2,10354000 -10410000,얇은 목걸이,Necklace,1,Normal,1,HIT,70,0,10410000 -10411000,얇은 목걸이(불),Necklace,1,Fire,2,HIT,81,0,10411000 -10412000,얇은 목걸이(물),Necklace,1,Water,3,HIT,99,0,10412000 -10413000,얇은 목걸이(땅),Necklace,1,Land,4,HIT,357,0,10413000 -10414000,얇은 목걸이(바람),Necklace,1,Wind,5,HIT,863,0,10414000 -10420000,수호의 목걸이,Necklace,2,Normal,6,HIT,192,0,10420000 -10421000,수호의 목걸이(불),Necklace,2,Fire,7,HIT,197,0,10421000 -10422000,수호의 목걸이(물),Necklace,2,Water,8,HIT,230,0,10422000 -10423000,수호의 목걸이(땅),Necklace,2,Land,9,HIT,1002,0,10423000 -10424000,수호의 목걸이(바람),Necklace,2,Wind,10,HIT,1682,0,10424000 -10430000,마력의 목걸이,Necklace,3,Normal,11,HIT,510,0,10430000 -10431000,마력의 목걸이(불),Necklace,3,Fire,12,HIT,522,0,10431000 -10432000,마력의 목걸이(물),Necklace,3,Water,13,HIT,608,0,10432000 -10433000,마력의 목걸이(땅),Necklace,3,Land,14,HIT,2151,0,10433000 -10434000,마력의 목걸이(바람),Necklace,3,Wind,15,HIT,2333,0,10434000 -10440000,전사의 목걸이,Necklace,4,Normal,11,HIT,2031,0,10440000 -10441000,전사의 목걸이(불),Necklace,4,Fire,12,HIT,3047,0,10441000 -10442000,전사의 목걸이(물),Necklace,4,Water,13,HIT,3047,0,10442000 -10443000,전사의 목걸이(땅),Necklace,4,Land,14,HIT,3047,0,10443000 -10444000,전사의 목걸이(바람),Necklace,4,Wind,15,HIT,3047,0,10444000 -10450000,전설의 목걸이,Necklace,4,Normal,11,HIT,7466,0,10450000 -10451000,전설의 목걸이(불),Necklace,4,Fire,12,HIT,4666,0,10451000 -10452000,전설의 목걸이(물),Necklace,4,Water,13,HIT,5366,0,10452000 -10453000,전설의 목걸이(땅),Necklace,4,Land,14,HIT,6066,0,10453000 -10454000,전설의 목걸이(바람),Necklace,4,Wind,15,HIT,6766,0,10454000 -10450001,고대의 목걸이,Necklace,5,Normal,15,HIT,15363,0,10450000 -10451001,고대의 목걸이(불),Necklace,5,Fire,15,HIT,10452,0,10451000 -10452001,고대의 목걸이(물),Necklace,5,Water,15,HIT,10452,0,10452000 -10453001,고대의 목걸이(땅),Necklace,5,Land,15,HIT,15363,0,10453000 -10454001,고대의 목걸이(바람),Necklace,5,Wind,15,HIT,15363,0,10454000 -10510000,얇은 반지,Ring,1,Normal,1,DEF,8,0,10510000 -10511000,얇은 반지(불),Ring,1,Fire,2,DEF,9,0,10511000 -10512000,얇은 반지(물),Ring,1,Water,3,DEF,11,0,10512000 -10513000,얇은 반지(땅),Ring,1,Land,4,DEF,44,0,10513000 -10514000,얇은 반지(바람),Ring,1,Wind,5,DEF,107,0,10514000 -10520000,수호의 반지,Ring,2,Normal,6,DEF,23,0,10520000 -10521000,수호의 반지(불),Ring,2,Fire,7,DEF,24,0,10521000 -10522000,수호의 반지(물),Ring,2,Water,8,DEF,29,0,10522000 -10523000,수호의 반지(땅),Ring,2,Land,9,DEF,122,0,10523000 -10524000,수호의 반지(바람),Ring,2,Wind,10,DEF,189,0,10524000 -10530000,마나 반지,Ring,3,Normal,11,DEF,63,0,10530000 -10531000,마나 반지(불),Ring,3,Fire,12,DEF,65,0,10531000 -10532000,마나 반지(물),Ring,3,Water,13,DEF,74,0,10532000 -10533000,마나 반지(땅),Ring,3,Land,14,DEF,251,0,10533000 -10534000,마나 반지(바람),Ring,3,Wind,15,DEF,270,0,10534000 -10540000,전사의 반지,Ring,4,Normal,11,DEF,864,0,10540000 -10541000,전사의 반지(불),Ring,4,Fire,12,DEF,540,0,10541000 -10542000,전사의 반지(물),Ring,4,Water,13,DEF,621,0,10542000 -10543000,전사의 반지(땅),Ring,4,Land,14,DEF,702,0,10543000 -10544000,전사의 반지(바람),Ring,4,Wind,15,DEF,783,0,10544000 -10550000,고대의 반지,Ring,5,Normal,11,DEF,1946,0,10550000 -10551000,고대의 반지(불),Ring,5,Fire,12,DEF,1209,0,10551000 -10552000,고대의 반지(물),Ring,5,Water,13,DEF,1209,0,10552000 -10553000,고대의 반지(땅),Ring,5,Land,14,DEF,1946,0,10553000 -10554000,고대의 반지(바람),Ring,5,Wind,15,DEF,1946,0,10554000 -11320000,블루 사파이어 벨트,Belt,2,Normal,6,SPD,176,2,11320000 -11420000,블루 사파이어 목걸이,Necklace,2,Normal,6,HIT,227,0,11420000 -11520000,블루 사파이어 반지,Ring,2,Normal,6,DEF,50,0,11520000 -12001001,Special Crystal Belt,Belt,2,Normal,0,ATK,1,0,12001001 -12001002,Special Crystal Necklace,Necklace,3,Normal,1,ATK,1,0,12001002 -12001003,Special Crystal Ring,Ring,4,Normal,2,ATK,1,0,12001003 -13001000,Aura,Aura,0,Normal,0,ATK,1,0,10100000 +id,_name,item_sub_type,grade,elemental_type,set_id,stat_type,stat_value,attack_range,spine_resource_path,exp +10100000,나뭇가지,Weapon,0,Normal,0,ATK,1,2,10100000,0 +10110000,검,Weapon,1,Normal,1,ATK,11,2,10110000,10 +10111000,롱 소드(불),Weapon,1,Fire,2,ATK,14,2,10111000,10 +10112000,롱 소드(물),Weapon,1,Water,3,ATK,17,2,10112000,10 +10113000,롱 소드(땅),Weapon,1,Land,4,ATK,55,2,10113000,10 +10114000,롱 소드(바람),Weapon,1,Wind,5,ATK,136,2,10114000,10 +10120000,검투사 검,Weapon,2,Normal,6,ATK,31,2,10120000,80 +10121000,검투사 검(불),Weapon,2,Fire,7,ATK,32,2,10121000,80 +10122000,검투사 검(물),Weapon,2,Water,8,ATK,38,2,10122000,80 +10123000,검투사 검(땅),Weapon,2,Land,9,ATK,152,2,10123000,80 +10124000,검투사 검(바람),Weapon,2,Wind,10,ATK,327,2,10124000,80 +10130000,까마귀의 검,Weapon,3,Normal,11,ATK,78,2,10130000,16020 +10131000,까마귀의 검(불),Weapon,3,Fire,12,ATK,81,2,10131000,16020 +10132000,까마귀의 검(물),Weapon,3,Water,13,ATK,92,2,10132000,16020 +10133000,까마귀의 검(땅),Weapon,3,Land,14,ATK,354,2,10133000,16020 +10134000,까마귀의 검(바람),Weapon,3,Wind,15,ATK,693,2,10134000,16020 +10130001,무거운 검,Weapon,3,Normal,11,ATK,170,2,10130001,16020 +10131001,무거운 검(불),Weapon,3,Fire,12,ATK,179,2,10131001,16020 +10132001,무거운 검(물),Weapon,3,Water,13,ATK,196,2,10132001,16020 +10133001,무거운 검(땅),Weapon,3,Land,14,ATK,908,2,10133001,16020 +10134001,무거운 검(바람),Weapon,3,Wind,15,ATK,967,2,10134001,16020 +10140000,전쟁 검,Weapon,4,Normal,11,ATK,3094,2,10140000,426000 +10141000,전쟁 검(불),Weapon,4,Fire,12,ATK,1934,2,10141000,426000 +10142000,전쟁 검(물),Weapon,4,Water,13,ATK,2224,2,10142000,426000 +10143000,전쟁 검(땅),Weapon,4,Land,14,ATK,2514,2,10143000,426000 +10144000,전쟁 검(바람),Weapon,4,Wind,15,ATK,2804,2,10144000,426000 +10150000,다인슬레이프,Weapon,5,Normal,16,ATK,6063,2,10150000,6760000 +10151000,다인슬레이프(불),Weapon,5,Fire,17,ATK,4331,2,10151000,6760000 +10152000,다인슬레이프(물),Weapon,5,Water,18,ATK,4331,2,10152000,6760000 +10153000,다인슬레이프(땅),Weapon,5,Land,19,ATK,6063,2,10153000,6760000 +10154000,다인슬레이프(바람),Weapon,5,Wind,20,ATK,6063,2,10154000,6760000 +10140001,아스가르드의 검,Weapon,5,Normal,11,ATK,765,2,10140001,6760000 +10141001,아스가르드의 검(불),Weapon,5,Fire,12,ATK,782,2,10141001,6760000 +10142001,아스가르드의 검(물),Weapon,5,Water,13,ATK,798,2,10142001,6760000 +10143001,아스가르드의 검(땅),Weapon,5,Land,14,ATK,1221,2,10143001,6760000 +10144001,아스가르드의 검(바람),Weapon,5,Wind,15,ATK,1525,2,10144001,6760000 +10150001,수르트의 검,Weapon,5,Normal,16,ATK,2157,2,10150001,6760000 +10151001,수르트의 검(불),Weapon,5,Fire,17,ATK,2190,2,10151001,6760000 +10152001,수르트의 검(물),Weapon,5,Water,18,ATK,2223,2,10152001,6760000 +10153001,수르트의 검(땅),Weapon,5,Land,19,ATK,3390,2,10153001,6760000 +10154001,수르트의 검(바람),Weapon,5,Wind,20,ATK,4219,2,10154001,6760000 +10155000,발키리의 검,Weapon,5,Normal,15,ATK,2902,2,10155000,6760000 +10200000,누더기 옷,Armor,0,Normal,0,HP,30,2,Character/Player/10200000,0 +10210000,옷,Armor,1,Normal,1,HP,180,2,Character/Player/10210000,19 +10211000,천 옷(불),Armor,1,Fire,2,HP,222,2,Character/Player/10211000,19 +10212000,천 옷(물),Armor,1,Water,3,HP,288,2,Character/Player/10212000,19 +10213000,천 옷(땅),Armor,1,Land,4,HP,915,2,Character/Player/10213000,19 +10214000,천 옷(바람),Armor,1,Wind,5,HP,2182,2,Character/Player/10214000,19 +10220000,가죽 옷,Armor,2,Normal,6,HP,531,2,Character/Player/10220000,103 +10221000,가죽 옷(불),Armor,2,Fire,7,HP,549,2,Character/Player/10221000,103 +10222000,가죽 옷(물),Armor,2,Water,8,HP,621,2,Character/Player/10222000,103 +10223000,가죽 옷(땅),Armor,2,Land,9,HP,2431,2,Character/Player/10223000,103 +10224000,가죽 옷(바람),Armor,2,Wind,10,HP,5109,2,Character/Player/10224000,103 +10230000,검은 까마귀의 갑옷,Armor,3,Normal,11,HP,1337,2,Character/Player/10230000,9600 +10231000,검은 까마귀의 갑옷(불),Armor,3,Fire,12,HP,1418,2,Character/Player/10231000,9600 +10232000,검은 까마귀의 갑옷(물),Armor,3,Water,13,HP,1580,2,Character/Player/10232000,9600 +10233000,검은 까마귀의 갑옷(땅),Armor,3,Land,14,HP,8370,2,Character/Player/10233000,9600 +10234000,검은 까마귀의 갑옷(바람),Armor,3,Wind,15,HP,10571,2,Character/Player/10234000,9600 +10230001,헤르메스의 옷,Armor,3,Normal,11,HP,2811,2,Character/Player/10230001,9600 +10231001,헤르메스의 옷(불),Armor,3,Fire,12,HP,3009,2,Character/Player/10231001,9600 +10232001,헤르메스의 옷(물),Armor,3,Water,13,HP,3141,2,Character/Player/10232001,9600 +10233001,헤르메스의 옷(땅),Armor,3,Land,14,HP,11482,2,Character/Player/10233001,9600 +10234001,헤르메스의 옷(바람),Armor,3,Wind,15,HP,12302,2,Character/Player/10234001,9600 +10240000,전쟁 갑옷,Armor,4,Normal,11,HP,34446,2,Character/Player/10240000,426000 +10241000,전쟁 갑옷(불),Armor,4,Fire,12,HP,24604,2,Character/Player/10241000,426000 +10242000,전쟁 갑옷(물),Armor,4,Water,13,HP,27064,2,Character/Player/10242000,426000 +10243000,전쟁 갑옷(땅),Armor,4,Land,14,HP,29525,2,Character/Player/10243000,426000 +10244000,전쟁 갑옷(바람),Armor,4,Wind,15,HP,31985,2,Character/Player/10244000,426000 +10250001,고대의 갑옷,Armor,5,Normal,11,HP,67513,2,Character/Player/10250001,6760000 +10251001,고대의 갑옷(불),Armor,5,Fire,12,HP,48224,2,Character/Player/10251001,6760000 +10252001,고대의 갑옷(물),Armor,5,Water,13,HP,48224,2,Character/Player/10252001,6760000 +10253001,고대의 갑옷(땅),Armor,5,Land,14,HP,67513,2,Character/Player/10253001,6760000 +10254001,고대의 갑옷(바람),Armor,5,Wind,15,HP,67513,2,Character/Player/10254001,6760000 +10250000,전설의 갑옷,Armor,5,Normal,11,HP,15550,2,Character/Player/10250000,6760000 +10251000,전설의 갑옷(불),Armor,5,Fire,12,HP,15848,2,Character/Player/10251000,6760000 +10252000,전설의 갑옷(물),Armor,5,Water,13,HP,16146,2,Character/Player/10252000,6760000 +10253000,전설의 갑옷(땅),Armor,5,Land,14,HP,24665,2,Character/Player/10253000,6760000 +10254000,전설의 갑옷(바람),Armor,5,Wind,15,HP,30823,2,Character/Player/10254000,6760000 +10240001,아스가르드의 갑옷,Armor,5,Normal,11,HP,10358,2,Character/Player/10240001,6760000 +10241001,아스가르드의 갑옷(불),Armor,5,Fire,12,HP,10571,2,Character/Player/10241001,6760000 +10242001,아스가르드의 갑옷(물),Armor,5,Water,13,HP,10783,2,Character/Player/10242001,6760000 +10243001,아스가르드의 갑옷(땅),Armor,5,Land,14,HP,16494,2,Character/Player/10243001,6760000 +10244001,아스가르드의 갑옷(바람),Armor,5,Wind,15,HP,20647,2,Character/Player/10244001,6760000 +10255000,천상의 고양이,Armor,5,Normal,11,HP,42856,2,Character/Player/10235000,6760000 +10310000,허리끈,Belt,1,Normal,1,SPD,46,2,10310000,954 +10311000,허리끈(불),Belt,1,Fire,2,SPD,62,2,10311000,954 +10312000,허리끈(물),Belt,1,Water,3,SPD,69,2,10312000,954 +10313000,허리끈(땅),Belt,1,Land,4,SPD,239,2,10313000,954 +10314000,허리끈(바람),Belt,1,Wind,5,SPD,610,2,10314000,954 +10320000,가죽 벨트,Belt,2,Normal,6,SPD,137,2,10320000,1496 +10321000,가죽 벨트(불),Belt,2,Fire,7,SPD,141,2,10321000,1496 +10322000,가죽 벨트(물),Belt,2,Water,8,SPD,166,2,10322000,1496 +10323000,가죽 벨트(땅),Belt,2,Land,9,SPD,718,2,10323000,1496 +10324000,가죽 벨트(바람),Belt,2,Wind,10,SPD,1314,2,10324000,1496 +10330000,견고한 벨트,Belt,3,Normal,11,SPD,350,2,10330000,3864 +10331000,견고한 벨트(불),Belt,3,Fire,12,SPD,359,2,10331000,3864 +10332000,견고한 벨트(물),Belt,3,Water,13,SPD,425,2,10332000,3864 +10333000,견고한 벨트(땅),Belt,3,Land,14,SPD,1701,2,10333000,3864 +10334000,견고한 벨트(바람),Belt,3,Wind,15,SPD,2883,2,10334000,3864 +10340000,전쟁 벨트,Belt,4,Normal,11,SPD,1215,2,10340000,96600 +10341000,전쟁 벨트(불),Belt,4,Fire,12,SPD,1262,2,10341000,96600 +10342000,전쟁 벨트(물),Belt,4,Water,13,SPD,2292,2,10342000,96600 +10343000,전쟁 벨트(땅),Belt,4,Land,14,SPD,2292,2,10343000,96600 +10344000,전쟁 벨트(바람),Belt,4,Wind,15,SPD,2292,2,10344000,96600 +10350000,전설의 벨트,Belt,4,Normal,11,SPD,7334,2,10350000,96600 +10351000,전설의 벨트(불),Belt,4,Fire,12,SPD,4584,2,10351000,96600 +10352000,전설의 벨트(물),Belt,4,Water,13,SPD,5272,2,10352000,96600 +10353000,전설의 벨트(땅),Belt,4,Land,14,SPD,5959,2,10353000,96600 +10354000,전설의 벨트(바람),Belt,4,Wind,15,SPD,6647,2,10354000,96600 +10350001,고대의 벨트,Belt,5,Normal,15,SPD,14373,2,10350000,6760000 +10351001,고대의 벨트(불),Belt,5,Fire,15,SPD,10267,2,10351000,6760000 +10352001,고대의 벨트(물),Belt,5,Water,15,SPD,10267,2,10352000,6760000 +10353001,고대의 벨트(땅),Belt,5,Land,15,SPD,14373,2,10353000,6760000 +10354001,고대의 벨트(바람),Belt,5,Wind,15,SPD,14373,2,10354000,6760000 +10410000,얇은 목걸이,Necklace,1,Normal,1,HIT,70,0,10410000,315 +10411000,얇은 목걸이(불),Necklace,1,Fire,2,HIT,81,0,10411000,315 +10412000,얇은 목걸이(물),Necklace,1,Water,3,HIT,99,0,10412000,315 +10413000,얇은 목걸이(땅),Necklace,1,Land,4,HIT,357,0,10413000,315 +10414000,얇은 목걸이(바람),Necklace,1,Wind,5,HIT,863,0,10414000,315 +10420000,수호의 목걸이,Necklace,2,Normal,6,HIT,192,0,10420000,1736 +10421000,수호의 목걸이(불),Necklace,2,Fire,7,HIT,197,0,10421000,1736 +10422000,수호의 목걸이(물),Necklace,2,Water,8,HIT,230,0,10422000,1736 +10423000,수호의 목걸이(땅),Necklace,2,Land,9,HIT,1002,0,10423000,1736 +10424000,수호의 목걸이(바람),Necklace,2,Wind,10,HIT,1682,0,10424000,1736 +10430000,마력의 목걸이,Necklace,3,Normal,11,HIT,510,0,10430000,65040 +10431000,마력의 목걸이(불),Necklace,3,Fire,12,HIT,522,0,10431000,65040 +10432000,마력의 목걸이(물),Necklace,3,Water,13,HIT,608,0,10432000,65040 +10433000,마력의 목걸이(땅),Necklace,3,Land,14,HIT,2151,0,10433000,65040 +10434000,마력의 목걸이(바람),Necklace,3,Wind,15,HIT,2333,0,10434000,65040 +10440000,전사의 목걸이,Necklace,4,Normal,11,HIT,2031,0,10440000,243000 +10441000,전사의 목걸이(불),Necklace,4,Fire,12,HIT,3047,0,10441000,243000 +10442000,전사의 목걸이(물),Necklace,4,Water,13,HIT,3047,0,10442000,243000 +10443000,전사의 목걸이(땅),Necklace,4,Land,14,HIT,3047,0,10443000,243000 +10444000,전사의 목걸이(바람),Necklace,4,Wind,15,HIT,3047,0,10444000,243000 +10450000,전설의 목걸이,Necklace,4,Normal,11,HIT,7466,0,10450000,243000 +10451000,전설의 목걸이(불),Necklace,4,Fire,12,HIT,4666,0,10451000,243000 +10452000,전설의 목걸이(물),Necklace,4,Water,13,HIT,5366,0,10452000,243000 +10453000,전설의 목걸이(땅),Necklace,4,Land,14,HIT,6066,0,10453000,243000 +10454000,전설의 목걸이(바람),Necklace,4,Wind,15,HIT,6766,0,10454000,243000 +10450001,고대의 목걸이,Necklace,5,Normal,15,HIT,15363,0,10450000,6760000 +10451001,고대의 목걸이(불),Necklace,5,Fire,15,HIT,10452,0,10451000,6760000 +10452001,고대의 목걸이(물),Necklace,5,Water,15,HIT,10452,0,10452000,6760000 +10453001,고대의 목걸이(땅),Necklace,5,Land,15,HIT,15363,0,10453000,6760000 +10454001,고대의 목걸이(바람),Necklace,5,Wind,15,HIT,15363,0,10454000,6760000 +10510000,얇은 반지,Ring,1,Normal,1,DEF,8,0,10510000,10 +10511000,얇은 반지(불),Ring,1,Fire,2,DEF,9,0,10511000,10 +10512000,얇은 반지(물),Ring,1,Water,3,DEF,11,0,10512000,10 +10513000,얇은 반지(땅),Ring,1,Land,4,DEF,44,0,10513000,10 +10514000,얇은 반지(바람),Ring,1,Wind,5,DEF,107,0,10514000,10 +10520000,수호의 반지,Ring,2,Normal,6,DEF,23,0,10520000,216 +10521000,수호의 반지(불),Ring,2,Fire,7,DEF,24,0,10521000,216 +10522000,수호의 반지(물),Ring,2,Water,8,DEF,29,0,10522000,216 +10523000,수호의 반지(땅),Ring,2,Land,9,DEF,122,0,10523000,216 +10524000,수호의 반지(바람),Ring,2,Wind,10,DEF,189,0,10524000,216 +10530000,마나 반지,Ring,3,Normal,11,DEF,63,0,10530000,5568 +10531000,마나 반지(불),Ring,3,Fire,12,DEF,65,0,10531000,5568 +10532000,마나 반지(물),Ring,3,Water,13,DEF,74,0,10532000,5568 +10533000,마나 반지(땅),Ring,3,Land,14,DEF,251,0,10533000,5568 +10534000,마나 반지(바람),Ring,3,Wind,15,DEF,270,0,10534000,5568 +10540000,전사의 반지,Ring,4,Normal,11,DEF,864,0,10540000,426000 +10541000,전사의 반지(불),Ring,4,Fire,12,DEF,540,0,10541000,426000 +10542000,전사의 반지(물),Ring,4,Water,13,DEF,621,0,10542000,426000 +10543000,전사의 반지(땅),Ring,4,Land,14,DEF,702,0,10543000,426000 +10544000,전사의 반지(바람),Ring,4,Wind,15,DEF,783,0,10544000,426000 +10550000,고대의 반지,Ring,5,Normal,11,DEF,1946,0,10550000,6760000 +10551000,고대의 반지(불),Ring,5,Fire,12,DEF,1209,0,10551000,6760000 +10552000,고대의 반지(물),Ring,5,Water,13,DEF,1209,0,10552000,6760000 +10553000,고대의 반지(땅),Ring,5,Land,14,DEF,1946,0,10553000,6760000 +10554000,고대의 반지(바람),Ring,5,Wind,15,DEF,1946,0,10554000,6760000 +11320000,블루 사파이어 벨트,Belt,2,Normal,6,SPD,176,2,11320000,1496 +11420000,블루 사파이어 목걸이,Necklace,2,Normal,6,HIT,227,0,11420000,1736 +11520000,블루 사파이어 반지,Ring,2,Normal,6,DEF,50,0,11520000,216 +12001001,Special Crystal Belt,Belt,2,Normal,0,ATK,1,0,12001001,1496 +12001002,Special Crystal Necklace,Necklace,3,Normal,1,ATK,1,0,12001002,65040 +12001003,Special Crystal Ring,Ring,4,Normal,2,ATK,1,0,12001003,426000 +13001000,Aura,Aura,0,Normal,0,ATK,1,0,10100000,0 diff --git a/Lib9c/TableData/Cost/EnhancementCostSheetV3.cs b/Lib9c/TableData/Cost/EnhancementCostSheetV3.cs new file mode 100644 index 0000000000..7f66acf82b --- /dev/null +++ b/Lib9c/TableData/Cost/EnhancementCostSheetV3.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Numerics; +using Nekoyume.Model.Item; + +namespace Nekoyume.TableData +{ + using static TableExtensions; + + public class EnhancementCostSheetV3 : Sheet + { + public class Row : EnhancementCostSheet.Row + { + public long Exp { get; private set; } + public int RequiredBlockIndex { get; private set; } + public int BaseStatGrowthMin { get; private set; } + public int BaseStatGrowthMax { get; private set; } + public int ExtraStatGrowthMin { get; private set; } + public int ExtraStatGrowthMax { get; private set; } + public int ExtraSkillDamageGrowthMin { get; private set; } + public int ExtraSkillDamageGrowthMax { get; private set; } + public int ExtraSkillChanceGrowthMin { get; private set; } + public int ExtraSkillChanceGrowthMax { get; private set; } + + public override void Set(IReadOnlyList fields) + { + base.Set(fields); + Exp = ParseLong(fields[5], 0); + RequiredBlockIndex = ParseInt(fields[6], 0); + BaseStatGrowthMin = ParseInt(fields[7], 0); + BaseStatGrowthMax = ParseInt(fields[8], 0); + ExtraStatGrowthMin = ParseInt(fields[9], 0); + ExtraStatGrowthMax = ParseInt(fields[10], 0); + ExtraSkillDamageGrowthMin = ParseInt(fields[11], 0); + ExtraSkillDamageGrowthMax = ParseInt(fields[12], 0); + ExtraSkillChanceGrowthMin = ParseInt(fields[13], 0); + ExtraSkillChanceGrowthMax = ParseInt(fields[14], 0); + } + } + + public EnhancementCostSheetV3() : base(nameof(EnhancementCostSheetV2)) + { + } + } +} diff --git a/Lib9c/TableData/Item/EquipmentItemSheet.cs b/Lib9c/TableData/Item/EquipmentItemSheet.cs index d9a84345cd..91fe1c5d31 100644 --- a/Lib9c/TableData/Item/EquipmentItemSheet.cs +++ b/Lib9c/TableData/Item/EquipmentItemSheet.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Linq; using Bencodex.Types; using Nekoyume.Model.Item; @@ -21,15 +20,22 @@ public class Row : ItemSheet.Row public DecimalStat Stat { get; private set; } public decimal AttackRange { get; private set; } public string SpineResourcePath { get; private set; } + public long? Exp { get; private set; } - public Row() {} + public Row() + { + } - public Row(Bencodex.Types.Dictionary serialized) : base(serialized) + public Row(Dictionary serialized) : base(serialized) { - SetId = (Integer) serialized["set_id"]; + SetId = (Integer)serialized["set_id"]; Stat = serialized["stat"].ToDecimalStat(); AttackRange = serialized["attack_range"].ToDecimal(); - SpineResourcePath = (Text) serialized["spine_resource_path"]; + SpineResourcePath = (Text)serialized["spine_resource_path"]; + if (serialized.ContainsKey("exp")) + { + Exp = serialized["exp"].ToLong(); + } } public override void Set(IReadOnlyList fields) @@ -37,23 +43,38 @@ public override void Set(IReadOnlyList fields) base.Set(fields); SetId = string.IsNullOrEmpty(fields[4]) ? 0 : ParseInt(fields[4]); Stat = new DecimalStat( - (StatType) Enum.Parse(typeof(StatType), fields[5]), + (StatType)Enum.Parse(typeof(StatType), fields[5]), ParseDecimal(fields[6])); AttackRange = ParseDecimal(fields[7]); SpineResourcePath = fields[8]; + if (fields.Count >= 10) + { + Exp = string.IsNullOrEmpty(fields[9]) ? 0L : ParseLong(fields[9]); + } } #pragma warning disable LAA1002 - public override IValue Serialize() => new Bencodex.Types.Dictionary(new Dictionary + public override IValue Serialize() { - [(Text) "set_id"] = (Integer) SetId, - [(Text) "stat"] = Stat.SerializeForLegacyEquipmentStat(), - [(Text) "attack_range"] = AttackRange.Serialize(), - [(Text) "spine_resource_path"] = (Text) SpineResourcePath, - }.Union((Bencodex.Types.Dictionary) base.Serialize())); + var pairs = new Dictionary + { + [(Text)"set_id"] = (Integer)SetId, + [(Text)"stat"] = Stat.SerializeForLegacyEquipmentStat(), + [(Text)"attack_range"] = AttackRange.Serialize(), + [(Text)"spine_resource_path"] = (Text)SpineResourcePath, + }; + if (!(Exp is null)) + { + pairs[(Text)"exp"] = Exp.Serialize(); + } + + return new Dictionary( + new Dictionary(pairs).Union((Dictionary)base.Serialize()) + ); + } #pragma warning restore LAA1002 } - + public EquipmentItemSheet() : base(nameof(EquipmentItemSheet)) { }