From 99ac84e135d3c9333233ce7e0dd663b1a2b9b16b Mon Sep 17 00:00:00 2001 From: Swen Mun Date: Wed, 3 Jan 2024 15:36:45 +0900 Subject: [PATCH 01/64] Remove unused policies --- Lib9c.Policy/Policy/BlockPolicySource.cs | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/Lib9c.Policy/Policy/BlockPolicySource.cs b/Lib9c.Policy/Policy/BlockPolicySource.cs index 37a35f9e08..4192752100 100644 --- a/Lib9c.Policy/Policy/BlockPolicySource.cs +++ b/Lib9c.Policy/Policy/BlockPolicySource.cs @@ -63,27 +63,6 @@ public IBlockPolicy GetInternalPolicy() => maxTransactionsPerBlockPolicy: MaxTransactionsPerBlockPolicy.Mainnet, maxTransactionsPerSignerPerBlockPolicy: MaxTransactionsPerSignerPerBlockPolicy.Internal); - /// - /// Creates an instance for 9c-permanent-test deployment. - /// - public IBlockPolicy GetPermanentPolicy() => - GetPolicy( - maxTransactionsBytesPolicy: MaxTransactionsBytesPolicy.Mainnet, - minTransactionsPerBlockPolicy: MinTransactionsPerBlockPolicy.Mainnet, - maxTransactionsPerBlockPolicy: MaxTransactionsPerBlockPolicy.Mainnet, - maxTransactionsPerSignerPerBlockPolicy: MaxTransactionsPerSignerPerBlockPolicy.Mainnet); - - /// - /// Creates an instance identical to the one deployed - /// except with lower minimum difficulty for faster testing and benchmarking. - /// - public IBlockPolicy GetTestPolicy() => - GetPolicy( - maxTransactionsBytesPolicy: MaxTransactionsBytesPolicy.Mainnet, - minTransactionsPerBlockPolicy: MinTransactionsPerBlockPolicy.Mainnet, - maxTransactionsPerBlockPolicy: MaxTransactionsPerBlockPolicy.Mainnet, - maxTransactionsPerSignerPerBlockPolicy: MaxTransactionsPerSignerPerBlockPolicy.Mainnet); - /// /// Creates an instance for networks /// with default options, without authorized mining and permissioned mining. From 07f72cdc4ceb7a02c97dde42908fbe24ed57ad57 Mon Sep 17 00:00:00 2001 From: Swen Mun Date: Wed, 3 Jan 2024 15:38:20 +0900 Subject: [PATCH 02/64] Rename predefined networks --- Lib9c.Policy/Policy/BlockPolicySource.cs | 16 ++++++++-------- .../Policy/MaxTransactionsBytesPolicy.cs | 4 ++-- .../Policy/MaxTransactionsPerBlockPolicy.cs | 2 +- .../MaxTransactionsPerSignerPerBlockPolicy.cs | 4 ++-- .../Policy/MinTransactionsPerBlockPolicy.cs | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Lib9c.Policy/Policy/BlockPolicySource.cs b/Lib9c.Policy/Policy/BlockPolicySource.cs index 4192752100..d14fb62922 100644 --- a/Lib9c.Policy/Policy/BlockPolicySource.cs +++ b/Lib9c.Policy/Policy/BlockPolicySource.cs @@ -48,20 +48,20 @@ public BlockPolicySource( /// public IBlockPolicy GetPolicy() => GetPolicy( - maxTransactionsBytesPolicy: MaxTransactionsBytesPolicy.Mainnet, - minTransactionsPerBlockPolicy: MinTransactionsPerBlockPolicy.Mainnet, - maxTransactionsPerBlockPolicy: MaxTransactionsPerBlockPolicy.Mainnet, - maxTransactionsPerSignerPerBlockPolicy: MaxTransactionsPerSignerPerBlockPolicy.Mainnet); + maxTransactionsBytesPolicy: MaxTransactionsBytesPolicy.Odin, + minTransactionsPerBlockPolicy: MinTransactionsPerBlockPolicy.Odin, + maxTransactionsPerBlockPolicy: MaxTransactionsPerBlockPolicy.Odin, + maxTransactionsPerSignerPerBlockPolicy: MaxTransactionsPerSignerPerBlockPolicy.Odin); /// /// Creates an instance for 9c-internal deployment. /// public IBlockPolicy GetInternalPolicy() => GetPolicy( - maxTransactionsBytesPolicy: MaxTransactionsBytesPolicy.Internal, - minTransactionsPerBlockPolicy: MinTransactionsPerBlockPolicy.Mainnet, - maxTransactionsPerBlockPolicy: MaxTransactionsPerBlockPolicy.Mainnet, - maxTransactionsPerSignerPerBlockPolicy: MaxTransactionsPerSignerPerBlockPolicy.Internal); + maxTransactionsBytesPolicy: MaxTransactionsBytesPolicy.OdinInternal, + minTransactionsPerBlockPolicy: MinTransactionsPerBlockPolicy.Odin, + maxTransactionsPerBlockPolicy: MaxTransactionsPerBlockPolicy.Odin, + maxTransactionsPerSignerPerBlockPolicy: MaxTransactionsPerSignerPerBlockPolicy.OdinInternal); /// /// Creates an instance for networks diff --git a/Lib9c.Policy/Policy/MaxTransactionsBytesPolicy.cs b/Lib9c.Policy/Policy/MaxTransactionsBytesPolicy.cs index 43dce29156..cacd6ca724 100644 --- a/Lib9c.Policy/Policy/MaxTransactionsBytesPolicy.cs +++ b/Lib9c.Policy/Policy/MaxTransactionsBytesPolicy.cs @@ -17,7 +17,7 @@ private MaxTransactionsBytesPolicy( public static IVariableSubPolicy Default => new MaxTransactionsBytesPolicy(long.MaxValue); - public static IVariableSubPolicy Mainnet => + public static IVariableSubPolicy Odin => Default // Note: The genesis block of 9c-main weighs 11,085,640 B (11 MiB). .Add(new SpannedSubPolicy( @@ -49,7 +49,7 @@ private MaxTransactionsBytesPolicy( value: 1024L * 500L)); // 500 KiB // Note: For internal testing. - public static IVariableSubPolicy Internal => + public static IVariableSubPolicy OdinInternal => Default .Add(new SpannedSubPolicy( startIndex: 0L, diff --git a/Lib9c.Policy/Policy/MaxTransactionsPerBlockPolicy.cs b/Lib9c.Policy/Policy/MaxTransactionsPerBlockPolicy.cs index 05995cf36b..c83f395f6a 100644 --- a/Lib9c.Policy/Policy/MaxTransactionsPerBlockPolicy.cs +++ b/Lib9c.Policy/Policy/MaxTransactionsPerBlockPolicy.cs @@ -17,7 +17,7 @@ private MaxTransactionsPerBlockPolicy( public static IVariableSubPolicy Default => new MaxTransactionsPerBlockPolicy(int.MaxValue); - public static IVariableSubPolicy Mainnet => + public static IVariableSubPolicy Odin => Default .Add(new SpannedSubPolicy( startIndex: 0, diff --git a/Lib9c.Policy/Policy/MaxTransactionsPerSignerPerBlockPolicy.cs b/Lib9c.Policy/Policy/MaxTransactionsPerSignerPerBlockPolicy.cs index a3a0d67bad..3394b411c6 100644 --- a/Lib9c.Policy/Policy/MaxTransactionsPerSignerPerBlockPolicy.cs +++ b/Lib9c.Policy/Policy/MaxTransactionsPerSignerPerBlockPolicy.cs @@ -17,7 +17,7 @@ private MaxTransactionsPerSignerPerBlockPolicy( public static IVariableSubPolicy Default => new MaxTransactionsPerSignerPerBlockPolicy(int.MaxValue); - public static IVariableSubPolicy Mainnet => + public static IVariableSubPolicy Odin => Default // Note: Introduced to prevent transactions spamming that may result in // the chain grinding to a halt without meaningful state transitions happening. @@ -29,7 +29,7 @@ private MaxTransactionsPerSignerPerBlockPolicy( value: 4)); // Note: For internal testing. - public static IVariableSubPolicy Internal => + public static IVariableSubPolicy OdinInternal => Default .Add(new SpannedSubPolicy( startIndex: 2_800_001, diff --git a/Lib9c.Policy/Policy/MinTransactionsPerBlockPolicy.cs b/Lib9c.Policy/Policy/MinTransactionsPerBlockPolicy.cs index 892b4153d2..2da5d7ccbc 100644 --- a/Lib9c.Policy/Policy/MinTransactionsPerBlockPolicy.cs +++ b/Lib9c.Policy/Policy/MinTransactionsPerBlockPolicy.cs @@ -17,7 +17,7 @@ private MinTransactionsPerBlockPolicy( public static IVariableSubPolicy Default => new MinTransactionsPerBlockPolicy(0); - public static IVariableSubPolicy Mainnet => + public static IVariableSubPolicy Odin => Default // Note: Introduced to prevent selfish mining where certain miners were // only mining empty blocks. Issued for v100050. From 65df3b4f07afb8e800d2477ab053951b3956c482 Mon Sep 17 00:00:00 2001 From: Swen Mun Date: Wed, 3 Jan 2024 16:33:14 +0900 Subject: [PATCH 03/64] Add OdinInternal --- Lib9c/Planet.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib9c/Planet.cs b/Lib9c/Planet.cs index 7722229a56..4dcd7cca7b 100644 --- a/Lib9c/Planet.cs +++ b/Lib9c/Planet.cs @@ -5,5 +5,6 @@ public enum Planet : byte Odin, Heimdall, Idun, + OdinInternal, } } From b82575eb0f4ca78f58677b0e096c1e8d684e7f52 Mon Sep 17 00:00:00 2001 From: Swen Mun Date: Tue, 9 Jan 2024 15:04:25 +0900 Subject: [PATCH 04/64] Remove warnings --- Lib9c.Policy/Policy/BlockPolicySource.cs | 9 ++++----- Lib9c.Policy/Policy/NCBlockPolicy.cs | 12 ++++++------ 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Lib9c.Policy/Policy/BlockPolicySource.cs b/Lib9c.Policy/Policy/BlockPolicySource.cs index d14fb62922..e52c4d339b 100644 --- a/Lib9c.Policy/Policy/BlockPolicySource.cs +++ b/Lib9c.Policy/Policy/BlockPolicySource.cs @@ -37,8 +37,7 @@ public partial class BlockPolicySource private readonly IActionLoader _actionLoader; - public BlockPolicySource( - IActionLoader? actionLoader = null) + public BlockPolicySource(IActionLoader? actionLoader = null) { _actionLoader = actionLoader ?? new NCActionLoader(); } @@ -138,11 +137,11 @@ internal IBlockPolicy GetPolicy( // Avoid NRE when genesis block appended long index = blockChain.Count > 0 ? blockChain.Tip.Index + 1: 0; - if (((ITransaction)transaction).Actions?.Count > 1) + if (transaction.Actions?.Count > 1) { return new TxPolicyViolationException( $"Transaction {transaction.Id} has too many actions: " + - $"{((ITransaction)transaction).Actions?.Count}", + $"{transaction.Actions?.Count}", transaction.Id); } else if (IsObsolete(transaction, actionLoader, index)) @@ -249,7 +248,7 @@ internal IBlockPolicy GetPolicy( catch (InvalidSignatureException) { return new TxPolicyViolationException( - $"Transaction {transaction.Id} has invalid signautre.", + $"Transaction {transaction.Id} has invalid signature.", transaction.Id); } diff --git a/Lib9c.Policy/Policy/NCBlockPolicy.cs b/Lib9c.Policy/Policy/NCBlockPolicy.cs index cfc2966d98..aa47725cd3 100644 --- a/Lib9c.Policy/Policy/NCBlockPolicy.cs +++ b/Lib9c.Policy/Policy/NCBlockPolicy.cs @@ -12,14 +12,14 @@ public class NCBlockPolicy : BlockPolicy public NCBlockPolicy( IAction blockAction, TimeSpan blockInterval, - Func + Func? validateNextBlockTx = null, - Func + Func? validateNextBlock = null, - Func getMaxTransactionsBytes = null, - Func getMinTransactionsPerBlock = null, - Func getMaxTransactionsPerBlock = null, - Func getMaxTransactionsPerSignerPerBlock = null) + Func? getMaxTransactionsBytes = null, + Func? getMinTransactionsPerBlock = null, + Func? getMaxTransactionsPerBlock = null, + Func? getMaxTransactionsPerSignerPerBlock = null) : base( blockAction: blockAction, blockInterval: blockInterval, From 3c37cfaa1a3661b47f566310865c634b23d0088d Mon Sep 17 00:00:00 2001 From: Swen Mun Date: Wed, 17 Jan 2024 20:31:10 +0900 Subject: [PATCH 05/64] Cleanup if statements --- Lib9c.Policy/Policy/BlockPolicySource.cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/Lib9c.Policy/Policy/BlockPolicySource.cs b/Lib9c.Policy/Policy/BlockPolicySource.cs index e52c4d339b..a3d914a2eb 100644 --- a/Lib9c.Policy/Policy/BlockPolicySource.cs +++ b/Lib9c.Policy/Policy/BlockPolicySource.cs @@ -268,26 +268,21 @@ internal IBlockPolicy GetPolicy( { return ibble; } - else if (ValidateTxCountPerBlockRaw( + + if (ValidateTxCountPerBlockRaw( nextBlock, minTransactionsPerBlockPolicy, maxTransactionsPerBlockPolicy) is InvalidBlockTxCountException ibtce) { return ibtce; } - else if (ValidateTxCountPerSignerPerBlockRaw( + + if (ValidateTxCountPerSignerPerBlockRaw( nextBlock, maxTransactionsPerSignerPerBlockPolicy) is InvalidBlockTxCountPerSignerException ibtcpse) { return ibtcpse; } - else - { - if (nextBlock.Index == 0) - { - return null; - } - } return null; } From 1ae6b6476ea65b780d74c2b561a08548ef4f3c79 Mon Sep 17 00:00:00 2001 From: Swen Mun Date: Thu, 1 Feb 2024 15:08:10 +0900 Subject: [PATCH 06/64] (Re)organize GetPolicy()s --- Lib9c.Policy/Policy/BlockPolicySource.cs | 50 ++++++++++++------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/Lib9c.Policy/Policy/BlockPolicySource.cs b/Lib9c.Policy/Policy/BlockPolicySource.cs index a3d914a2eb..6e9c12244c 100644 --- a/Lib9c.Policy/Policy/BlockPolicySource.cs +++ b/Lib9c.Policy/Policy/BlockPolicySource.cs @@ -43,35 +43,35 @@ public BlockPolicySource(IActionLoader? actionLoader = null) } /// - /// Creates an instance for 9c-main deployment. + /// Creates an instance for Odin mainnet. /// - public IBlockPolicy GetPolicy() => - GetPolicy( - maxTransactionsBytesPolicy: MaxTransactionsBytesPolicy.Odin, - minTransactionsPerBlockPolicy: MinTransactionsPerBlockPolicy.Odin, - maxTransactionsPerBlockPolicy: MaxTransactionsPerBlockPolicy.Odin, - maxTransactionsPerSignerPerBlockPolicy: MaxTransactionsPerSignerPerBlockPolicy.Odin); + public IBlockPolicy GetPolicy() => GetPolicy(Planet.Odin); /// - /// Creates an instance for 9c-internal deployment. + /// Creates an instance for the given planet. /// - public IBlockPolicy GetInternalPolicy() => - GetPolicy( - maxTransactionsBytesPolicy: MaxTransactionsBytesPolicy.OdinInternal, - minTransactionsPerBlockPolicy: MinTransactionsPerBlockPolicy.Odin, - maxTransactionsPerBlockPolicy: MaxTransactionsPerBlockPolicy.Odin, - maxTransactionsPerSignerPerBlockPolicy: MaxTransactionsPerSignerPerBlockPolicy.OdinInternal); - - /// - /// Creates an instance for networks - /// with default options, without authorized mining and permissioned mining. - /// - public IBlockPolicy GetDefaultPolicy() => - GetPolicy( - maxTransactionsBytesPolicy: MaxTransactionsBytesPolicy.Default, - minTransactionsPerBlockPolicy: MinTransactionsPerBlockPolicy.Default, - maxTransactionsPerBlockPolicy: MaxTransactionsPerBlockPolicy.Default, - maxTransactionsPerSignerPerBlockPolicy: MaxTransactionsPerSignerPerBlockPolicy.Default); + public IBlockPolicy GetPolicy(Planet planet) + { + return planet switch + { + Planet.Odin => GetPolicy( + maxTransactionsBytesPolicy: MaxTransactionsBytesPolicy.Odin, + minTransactionsPerBlockPolicy: MinTransactionsPerBlockPolicy.Odin, + maxTransactionsPerBlockPolicy: MaxTransactionsPerBlockPolicy.Odin, + maxTransactionsPerSignerPerBlockPolicy: MaxTransactionsPerSignerPerBlockPolicy.Odin + ), + Planet.OdinInternal => GetPolicy( + maxTransactionsBytesPolicy: MaxTransactionsBytesPolicy.OdinInternal, + minTransactionsPerBlockPolicy: MinTransactionsPerBlockPolicy.Odin, + maxTransactionsPerBlockPolicy: MaxTransactionsPerBlockPolicy.Odin, + maxTransactionsPerSignerPerBlockPolicy: MaxTransactionsPerSignerPerBlockPolicy.OdinInternal + ), + _ => throw new ArgumentException( + $"Can't retrieve policy for given planet ({planet})", + nameof(planet) + ), + }; + } /// /// Gets a constructed from given parameters. From 16080009144c2d062832da6cea466c2e337dd12e Mon Sep 17 00:00:00 2001 From: Swen Mun Date: Thu, 1 Feb 2024 16:19:41 +0900 Subject: [PATCH 07/64] Add Heimdall case on GetPolicy() --- Lib9c.Policy/Policy/BlockPolicySource.cs | 6 ++++++ Lib9c.Policy/Policy/MaxTransactionsBytesPolicy.cs | 13 ++++++++++++- .../Policy/MaxTransactionsPerBlockPolicy.cs | 6 ++++++ .../MaxTransactionsPerSignerPerBlockPolicy.cs | 6 ++++++ .../Policy/MinTransactionsPerBlockPolicy.cs | 2 ++ 5 files changed, 32 insertions(+), 1 deletion(-) diff --git a/Lib9c.Policy/Policy/BlockPolicySource.cs b/Lib9c.Policy/Policy/BlockPolicySource.cs index 6e9c12244c..1da3a53f98 100644 --- a/Lib9c.Policy/Policy/BlockPolicySource.cs +++ b/Lib9c.Policy/Policy/BlockPolicySource.cs @@ -66,6 +66,12 @@ public IBlockPolicy GetPolicy(Planet planet) maxTransactionsPerBlockPolicy: MaxTransactionsPerBlockPolicy.Odin, maxTransactionsPerSignerPerBlockPolicy: MaxTransactionsPerSignerPerBlockPolicy.OdinInternal ), + Planet.Heimdall => GetPolicy( + maxTransactionsBytesPolicy: MaxTransactionsBytesPolicy.Heimdall, + minTransactionsPerBlockPolicy: MinTransactionsPerBlockPolicy.Heimdall, + maxTransactionsPerBlockPolicy: MaxTransactionsPerBlockPolicy.Heimdall, + maxTransactionsPerSignerPerBlockPolicy: MaxTransactionsPerSignerPerBlockPolicy.Heimdall + ), _ => throw new ArgumentException( $"Can't retrieve policy for given planet ({planet})", nameof(planet) diff --git a/Lib9c.Policy/Policy/MaxTransactionsBytesPolicy.cs b/Lib9c.Policy/Policy/MaxTransactionsBytesPolicy.cs index cacd6ca724..d2d47aa51a 100644 --- a/Lib9c.Policy/Policy/MaxTransactionsBytesPolicy.cs +++ b/Lib9c.Policy/Policy/MaxTransactionsBytesPolicy.cs @@ -19,7 +19,7 @@ private MaxTransactionsBytesPolicy( public static IVariableSubPolicy Odin => Default - // Note: The genesis block of 9c-main weighs 11,085,640 B (11 MiB). + // Note: The genesis block of Odin weighs 11,085,640 B (11 MiB). .Add(new SpannedSubPolicy( startIndex: 0L, value: 1024L * 1024L * 15L)) // 15 MiB @@ -48,6 +48,17 @@ private MaxTransactionsBytesPolicy( startIndex: 3_150_001L, value: 1024L * 500L)); // 500 KiB + public static IVariableSubPolicy Heimdall => + Default + // Note: The genesis block of Heimdall weights 4,700,853 B (4.5 MiB). + .Add(new SpannedSubPolicy( + startIndex: 0L, + value: 1024L * 1024L * 5L)) // 5 MiB + // Note: Heimdall has been started after v100098 + .Add(new SpannedSubPolicy( + startIndex: 1L, + value: 1024L * 500L)); // 500 KiB + // Note: For internal testing. public static IVariableSubPolicy OdinInternal => Default diff --git a/Lib9c.Policy/Policy/MaxTransactionsPerBlockPolicy.cs b/Lib9c.Policy/Policy/MaxTransactionsPerBlockPolicy.cs index c83f395f6a..67bbf5dd61 100644 --- a/Lib9c.Policy/Policy/MaxTransactionsPerBlockPolicy.cs +++ b/Lib9c.Policy/Policy/MaxTransactionsPerBlockPolicy.cs @@ -22,5 +22,11 @@ private MaxTransactionsPerBlockPolicy( .Add(new SpannedSubPolicy( startIndex: 0, value: BlockPolicySource.MaxTransactionsPerBlock)); + + public static IVariableSubPolicy Heimdall => + Default + .Add(new SpannedSubPolicy( + startIndex: 0, + value: BlockPolicySource.MaxTransactionsPerBlock)); } } diff --git a/Lib9c.Policy/Policy/MaxTransactionsPerSignerPerBlockPolicy.cs b/Lib9c.Policy/Policy/MaxTransactionsPerSignerPerBlockPolicy.cs index 3394b411c6..d5f0383fa9 100644 --- a/Lib9c.Policy/Policy/MaxTransactionsPerSignerPerBlockPolicy.cs +++ b/Lib9c.Policy/Policy/MaxTransactionsPerSignerPerBlockPolicy.cs @@ -34,5 +34,11 @@ private MaxTransactionsPerSignerPerBlockPolicy( .Add(new SpannedSubPolicy( startIndex: 2_800_001, value: 4)); + + public static IVariableSubPolicy Heimdall => + Default + .Add(new SpannedSubPolicy( + startIndex: 1, + value: 4)); } } diff --git a/Lib9c.Policy/Policy/MinTransactionsPerBlockPolicy.cs b/Lib9c.Policy/Policy/MinTransactionsPerBlockPolicy.cs index 2da5d7ccbc..76fe3d308f 100644 --- a/Lib9c.Policy/Policy/MinTransactionsPerBlockPolicy.cs +++ b/Lib9c.Policy/Policy/MinTransactionsPerBlockPolicy.cs @@ -31,5 +31,7 @@ private MinTransactionsPerBlockPolicy( startIndex: 3_924_700, value: 0 )); + + public static IVariableSubPolicy Heimdall => Default; } } From f47f4f53ffedb086ba1617b8af177d7875eca2b2 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Thu, 18 Jan 2024 17:47:49 +0900 Subject: [PATCH 08/64] Introduce CollectionState --- .../Model/State/CollectionStateTest.cs | 44 +++++++++++++++++++ Lib9c/Model/State/CollectionState.cs | 40 +++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 .Lib9c.Tests/Model/State/CollectionStateTest.cs create mode 100644 Lib9c/Model/State/CollectionState.cs diff --git a/.Lib9c.Tests/Model/State/CollectionStateTest.cs b/.Lib9c.Tests/Model/State/CollectionStateTest.cs new file mode 100644 index 0000000000..e4cd03ac95 --- /dev/null +++ b/.Lib9c.Tests/Model/State/CollectionStateTest.cs @@ -0,0 +1,44 @@ +namespace Lib9c.Tests.Model.State +{ + using System.Collections.Generic; + using Bencodex.Types; + using Libplanet.Crypto; + using Nekoyume.Action; + using Nekoyume.Model.State; + using Xunit; + + public class CollectionStateTest + { + [Fact] + public void Derive() + { + var address = new PrivateKey().Address; + Assert.Equal(address.Derive(nameof(CollectionState)), CollectionState.Derive(address)); + } + + [Fact] + public void Serialize() + { + var address = new PrivateKey().Address; + var state = new CollectionState + { + Address = address, + Ids = new List + { + 1, + 2, + }, + }; + + var serialized = (List)state.Serialize(); + var expected = List.Empty + .Add(address.Serialize()) + .Add(List.Empty.Add(1).Add(2)); + Assert.Equal(expected, serialized); + + var deserialized = new CollectionState(serialized); + Assert.Equal(state.Address, deserialized.Address); + Assert.Equal(state.Ids, deserialized.Ids); + } + } +} diff --git a/Lib9c/Model/State/CollectionState.cs b/Lib9c/Model/State/CollectionState.cs new file mode 100644 index 0000000000..a0d9663267 --- /dev/null +++ b/Lib9c/Model/State/CollectionState.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using Bencodex.Types; +using Libplanet.Crypto; +using Nekoyume.Action; + +namespace Nekoyume.Model.State +{ + /// + /// Represents the state of a collection. + /// + public class CollectionState + { + public static Address Derive(Address avatarAddress) => + avatarAddress.Derive(nameof(CollectionState)); + + public Address Address; + public List Ids = new(); + + public CollectionState() + { + } + + public CollectionState(List serialized) + { + Address = serialized[0].ToAddress(); + var rawList = (List) serialized[1]; + foreach (var value in rawList) + { + Ids.Add((Integer)value); + } + } + + public IValue Serialize() + { + return List.Empty + .Add(Address.Serialize()) + .Add(new List(Ids)); + } + } +} From b81eb1e125f8ef9a7ecef8b59947d01d0dc93d66 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Thu, 18 Jan 2024 17:48:02 +0900 Subject: [PATCH 09/64] Introduce CollectionSheet --- .Lib9c.Tests/TableData/CollectionSheetTest.cs | 42 +++++++++++ Lib9c/TableCSV/CollectionSheet.csv | 3 + Lib9c/TableData/CollectionSheet.cs | 69 +++++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 .Lib9c.Tests/TableData/CollectionSheetTest.cs create mode 100644 Lib9c/TableCSV/CollectionSheet.csv create mode 100644 Lib9c/TableData/CollectionSheet.cs diff --git a/.Lib9c.Tests/TableData/CollectionSheetTest.cs b/.Lib9c.Tests/TableData/CollectionSheetTest.cs new file mode 100644 index 0000000000..8641ed9a38 --- /dev/null +++ b/.Lib9c.Tests/TableData/CollectionSheetTest.cs @@ -0,0 +1,42 @@ +namespace Lib9c.Tests.TableData +{ + using Nekoyume.Model.Stat; + using Nekoyume.TableData; + using Xunit; + + public class CollectionSheetTest + { + [Fact] + public void Set() + { + const string csv = @"id,item_id,count,level,option_count,skill,item_id,count,level,option_count,skillitem_id,count,level,option_count,skillitem_id,count,level,option_count,skillitem_id,count,level,option_count,skill,item_id,count,level,option_count,skill,stat_type,modify_type,modify_value,stat_type,modify_type,modify_value,stat_type,modify_type,modify_value\n +1,1,2,3,4,,,,,,,,,,,,,,,,,,,,,,,,,,,ATK,Add,1,,,,,,\n +2,2,3,4,5,,,,,,,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,1,,,,,,\n"; + var sheet = new CollectionSheet(); + sheet.Set(csv); + + for (int i = 0; i < 1; i++) + { + var id = i + 1; + var row = sheet[id]; + Assert.Equal(id, row.Id); + Assert.Equal(id, row.Materials.Count); + Assert.Equal(id, row.StatModifiers.Count); + for (int j = 0; j < id; j++) + { + var material = row.Materials[j]; + Assert.Equal(id, material.ItemId); + Assert.Equal(id + j + 1, material.Count); + Assert.Equal(id + j + 2, material.Level); + Assert.Equal(id + j + 3, material.OptionCount); + Assert.False(material.SkillContains); + + var modifier = row.StatModifiers[j]; + Assert.Equal(StatType.ATK, modifier.StatType); + Assert.Equal(id + j, modifier.Value); + Assert.Equal(j, (int)modifier.Operation); + } + } + } + } +} diff --git a/Lib9c/TableCSV/CollectionSheet.csv b/Lib9c/TableCSV/CollectionSheet.csv new file mode 100644 index 0000000000..b72b50d9e8 --- /dev/null +++ b/Lib9c/TableCSV/CollectionSheet.csv @@ -0,0 +1,3 @@ +id,item_id,count,level,option_count,skill,item_id,count,level,option_count,skillitem_id,count,level,option_count,skillitem_id,count,level,option_count,skillitem_id,count,level,option_count,skill,item_id,count,level,option_count,skill,stat_type,modify_type,modify_value,stat_type,modify_type,modify_value,stat_type,modify_type,modify_value +1,10110000,1,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,ATK,Add,1,,,,,, +2,10110000,1,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,1,,,,,, diff --git a/Lib9c/TableData/CollectionSheet.cs b/Lib9c/TableData/CollectionSheet.cs new file mode 100644 index 0000000000..6b7b78b538 --- /dev/null +++ b/Lib9c/TableData/CollectionSheet.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using Nekoyume.Model.Stat; +using static Nekoyume.TableData.TableExtensions; + +namespace Nekoyume.TableData +{ + public class CollectionSheet : Sheet + { + + public class CollectionMaterial + { + public int ItemId; + public int Count; + public int Level; + public int OptionCount; + public bool SkillContains; + } + + public class Row : SheetRow + { + public override int Key => Id; + + public int Id { get; private set; } + + public List Materials = new(); + + public List StatModifiers = new(); + public override void Set(IReadOnlyList fields) + { + Id = ParseInt(fields[0]); + for (int i = 0; i < 6; i++) + { + var offset = i * 5; + if (!TryParseInt(fields[1 + offset], out var itemId) || itemId == 0) + { + continue; + } + Materials.Add(new CollectionMaterial + { + ItemId = itemId, + Count = ParseInt(fields[2 + offset]), + Level = ParseInt(fields[3 + offset]), + OptionCount = ParseInt(fields[4 + offset], 0), + SkillContains = ParseBool(fields[5 + offset], false) + }); + } + + for (int i = 0; i < 3; i++) + { + var offset = i * 3; + var statType = fields[28 + offset]; + if (string.IsNullOrEmpty(statType)) + { + continue; + } + StatModifiers.Add(new StatModifier( + (StatType) Enum.Parse(typeof(StatType), statType), + (StatModifier.OperationType) Enum.Parse(typeof(StatModifier.OperationType), fields[29 + offset]), + ParseInt(fields[30 + offset]))); + } + } + } + + public CollectionSheet() : base(nameof(CollectionSheet)) + { + } + } +} From b69d0af5ae5dc3e521672a3ffd6bb0eff72bbdd0 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Mon, 22 Jan 2024 19:03:42 +0900 Subject: [PATCH 10/64] Introduce ActivateCollection --- .Lib9c.Tests/Action/ActivateCollectionTest.cs | 123 ++++++++++++++++++ .Lib9c.Tests/TableSheets.cs | 2 + Lib9c/Action/ActivateCollection.cs | 114 ++++++++++++++++ Lib9c/Model/Collection/CollectionFactory.cs | 18 +++ .../Collection/FungibleCollectionMaterial.cs | 29 +++++ Lib9c/Model/Collection/ICollectionMaterial.cs | 13 ++ Lib9c/Model/Collection/MaterialType.cs | 8 ++ .../NonFungibleCollectionMaterial.cs | 45 +++++++ Lib9c/Model/Item/Inventory.cs | 53 ++++++++ Lib9c/TableCSV/CollectionSheet.csv | 4 +- Lib9c/TableData/CollectionSheet.cs | 19 ++- 11 files changed, 425 insertions(+), 3 deletions(-) create mode 100644 .Lib9c.Tests/Action/ActivateCollectionTest.cs create mode 100644 Lib9c/Action/ActivateCollection.cs create mode 100644 Lib9c/Model/Collection/CollectionFactory.cs create mode 100644 Lib9c/Model/Collection/FungibleCollectionMaterial.cs create mode 100644 Lib9c/Model/Collection/ICollectionMaterial.cs create mode 100644 Lib9c/Model/Collection/MaterialType.cs create mode 100644 Lib9c/Model/Collection/NonFungibleCollectionMaterial.cs diff --git a/.Lib9c.Tests/Action/ActivateCollectionTest.cs b/.Lib9c.Tests/Action/ActivateCollectionTest.cs new file mode 100644 index 0000000000..d7e0e31f81 --- /dev/null +++ b/.Lib9c.Tests/Action/ActivateCollectionTest.cs @@ -0,0 +1,123 @@ +namespace Lib9c.Tests.Action +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Bencodex.Types; + using Libplanet.Action; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Nekoyume; + using Nekoyume.Action; + using Nekoyume.Model.Collection; + using Nekoyume.Model.Item; + using Nekoyume.Model.State; + using Nekoyume.TableData; + using Xunit; + using static SerializeKeys; + + public class ActivateCollectionTest + { + private readonly IAccount _initialState; + private readonly Address _agentAddress; + private readonly Address _avatarAddress; + private readonly TableSheets _tableSheets; + + public ActivateCollectionTest() + { + var sheets = TableSheetsImporter.ImportSheets(); + _tableSheets = new TableSheets(sheets); + + var privateKey = new PrivateKey(); + _agentAddress = privateKey.PublicKey.Address; + var agentState = new AgentState(_agentAddress); + + _avatarAddress = _agentAddress.Derive("avatar"); + var gameConfigState = new GameConfigState(sheets[nameof(GameConfigSheet)]); + var avatarState = new AvatarState( + _avatarAddress, + _agentAddress, + 0, + _tableSheets.GetAvatarSheets(), + gameConfigState, + default + ) + { + level = 100, + }; + var inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); + var worldInformationAddress = _avatarAddress.Derive(LegacyWorldInformationKey); + var questListAddress = _avatarAddress.Derive(LegacyQuestListKey); + agentState.avatarAddresses.Add(0, _avatarAddress); + + _initialState = new Account(MockState.Empty) + .SetState(_agentAddress, agentState.SerializeV2()) + .SetState(_avatarAddress, avatarState.SerializeV2()) + .SetState(inventoryAddress, avatarState.inventory.Serialize()) + .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) + .SetState(questListAddress, avatarState.questList.Serialize()) + .SetState(gameConfigState.address, gameConfigState.Serialize()); + + foreach (var (key, value) in sheets) + { + _initialState = _initialState + .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); + } + } + + [Fact] + public void Execute() + { + var row = _tableSheets.CollectionSheet.Values.First(); + var avatarState = _initialState.GetAvatarStateV2(_avatarAddress); + var materials = new List(); + foreach (var material in row.Materials) + { + var itemRow = _tableSheets.ItemSheet[material.ItemId]; + var item = ItemFactory.CreateItem(itemRow, new TestRandom()); + avatarState.inventory.AddItem(item, material.Count); + if (item is ItemUsable itemUsable) + { + materials.Add(new NonFungibleCollectionMaterial + { + ItemId = item.Id, + NonFungibleId = itemUsable.NonFungibleId, + OptionCount = material.OptionCount, + SkillContains = material.SkillContains, + }); + } + else + { + materials.Add(new FungibleCollectionMaterial + { + ItemId = item.Id, + ItemCount = material.Count, + }); + } + } + + var inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); + var state = _initialState.SetState(inventoryAddress, avatarState.inventory.Serialize()); + IActionContext context = new ActionContext() + { + PreviousState = state, + Signer = _agentAddress, + }; + ActivateCollection activateCollection = new ActivateCollection() + { + AvatarAddress = _avatarAddress, + CollectionId = row.Id, + Materials = materials, + }; + + var nextState = activateCollection.Execute(context); + var collectionAddress = CollectionState.Derive(_avatarAddress); + var rawList = Assert.IsType(nextState.GetState(collectionAddress)); + var collectionState = new CollectionState(rawList); + Assert.Equal(row.Id, collectionState.Ids.Single()); + + var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); + Assert.Empty(nextAvatarState.inventory.Items); + } + } +} diff --git a/.Lib9c.Tests/TableSheets.cs b/.Lib9c.Tests/TableSheets.cs index dcc3ee435f..67d5497355 100644 --- a/.Lib9c.Tests/TableSheets.cs +++ b/.Lib9c.Tests/TableSheets.cs @@ -246,6 +246,8 @@ public StakeActionPointCoefficientSheet StakeActionPointCoefficientSheet public CreateAvatarFavSheet CreateAvatarFavSheet { get; set; } + public CollectionSheet CollectionSheet { get; private set; } + public void ItemSheetInitialize() { ItemSheet ??= new ItemSheet(); diff --git a/Lib9c/Action/ActivateCollection.cs b/Lib9c/Action/ActivateCollection.cs new file mode 100644 index 0000000000..4532b6f27b --- /dev/null +++ b/Lib9c/Action/ActivateCollection.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Bencodex.Types; +using Libplanet.Action; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Nekoyume.Extensions; +using Nekoyume.Model.Collection; +using Nekoyume.Model.Item; +using Nekoyume.Model.State; +using Nekoyume.TableData; +using static Lib9c.SerializeKeys; + +namespace Nekoyume.Action +{ + [ActionType("activate_collection")] + public class ActivateCollection: GameAction + { + public Address AvatarAddress; + public int CollectionId; + public List Materials = new(); + public override IAccount Execute(IActionContext context) + { + context.UseGas(1); + var states = context.PreviousState; + if (states.TryGetAvatarStateV2(context.Signer, AvatarAddress, out var avatarState, + out _)) + { + var sheets = states.GetSheets(containItemSheet: true, sheetTypes: new[] + { + typeof(CollectionSheet) + }); + var collectionSheet = sheets.GetSheet(); + var row = collectionSheet[CollectionId]; + var materials = Materials; + foreach (var materialInfo in row.Materials) + { + var material = materials.FirstOrDefault(m => + m.ItemId == materialInfo.ItemId && m.ItemCount == materialInfo.Count); + if (material is null) + { + throw new Exception(); + } + switch (material) + { + case FungibleCollectionMaterial fungibleCollectionMaterial: + if (!avatarState.inventory.RemoveMaterial(materialInfo.ItemId, + materialInfo.Count)) + { + throw new Exception(); + } + break; + case NonFungibleCollectionMaterial nonFungibleCollectionMaterial: + var nonFungibleId = nonFungibleCollectionMaterial.NonFungibleId; + if (avatarState.inventory.TryGetNonFungibleItem(nonFungibleId, + out ItemUsable materialItem) && materialInfo.Validate(materialItem)) + { + avatarState.inventory.RemoveNonFungibleItem(materialItem); + } + else + { + throw new Exception(); + } + break; + default: + throw new ArgumentOutOfRangeException(nameof(material)); + } + + materials.Remove(material); + } + + if (materials.Any()) + { + throw new Exception(); + } + + var collectionAddress = CollectionState.Derive(AvatarAddress); + var collectionState = states.TryGetState(collectionAddress, out List rawState) + ? new CollectionState(rawState) + : new CollectionState + { + Address = collectionAddress + }; + collectionState.Ids.Add(CollectionId); + var inventoryAddress = AvatarAddress.Derive(LegacyInventoryKey); + return states + .SetState(inventoryAddress, avatarState.inventory.Serialize()) + .SetState(collectionAddress, collectionState.Serialize()); + } + + throw new FailedLoadStateException(AvatarAddress, typeof(Dictionary)); + } + + protected override IImmutableDictionary PlainValueInternal => + new Dictionary + { + ["a"] = AvatarAddress.Serialize(), + ["c"] = (Integer)CollectionId, + ["m"] = new List(Materials.Select(i => i.Serialize())), + }.ToImmutableDictionary(); + protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) + { + AvatarAddress = plainValue["a"].ToAddress(); + CollectionId = (Integer)plainValue["c"]; + var list = (List) plainValue["m"]; + foreach (var innerList in list) + { + Materials.Add(CollectionFactory.DeserializeMaterial((List)innerList)); + } + } + } +} diff --git a/Lib9c/Model/Collection/CollectionFactory.cs b/Lib9c/Model/Collection/CollectionFactory.cs new file mode 100644 index 0000000000..b190ea57cd --- /dev/null +++ b/Lib9c/Model/Collection/CollectionFactory.cs @@ -0,0 +1,18 @@ +using System; +using Bencodex.Types; + +namespace Nekoyume.Model.Collection +{ + public static class CollectionFactory + { + public static ICollectionMaterial DeserializeMaterial(List serialized) + { + if ((Integer)serialized[0] == (int)MaterialType.Fungible) + { + return new FungibleCollectionMaterial(serialized); + } + + return new NonFungibleCollectionMaterial(serialized); + } + } +} diff --git a/Lib9c/Model/Collection/FungibleCollectionMaterial.cs b/Lib9c/Model/Collection/FungibleCollectionMaterial.cs new file mode 100644 index 0000000000..3109edf8bd --- /dev/null +++ b/Lib9c/Model/Collection/FungibleCollectionMaterial.cs @@ -0,0 +1,29 @@ +using Bencodex.Types; + +namespace Nekoyume.Model.Collection +{ + public class FungibleCollectionMaterial : ICollectionMaterial + { + public MaterialType Type => MaterialType.Fungible; + public int ItemId { get; set; } + public int ItemCount { get; set; } + + public IValue Serialize() + { + return List.Empty + .Add((int)Type) + .Add(ItemId) + .Add(ItemCount); + } + + public FungibleCollectionMaterial(List serialized) + { + ItemId = (Integer)serialized[1]; + ItemCount = (Integer)serialized[2]; + } + + public FungibleCollectionMaterial() + { + } + } +} diff --git a/Lib9c/Model/Collection/ICollectionMaterial.cs b/Lib9c/Model/Collection/ICollectionMaterial.cs new file mode 100644 index 0000000000..c4612bbec2 --- /dev/null +++ b/Lib9c/Model/Collection/ICollectionMaterial.cs @@ -0,0 +1,13 @@ +using Bencodex.Types; + +namespace Nekoyume.Model.Collection +{ + public interface ICollectionMaterial + { + public MaterialType Type { get; } + public int ItemId { get; set; } + public int ItemCount { get; set; } + + public IValue Serialize(); + } +} diff --git a/Lib9c/Model/Collection/MaterialType.cs b/Lib9c/Model/Collection/MaterialType.cs new file mode 100644 index 0000000000..3be047ca99 --- /dev/null +++ b/Lib9c/Model/Collection/MaterialType.cs @@ -0,0 +1,8 @@ +namespace Nekoyume.Model.Collection +{ + public enum MaterialType + { + Fungible, + NonFungible + } +} diff --git a/Lib9c/Model/Collection/NonFungibleCollectionMaterial.cs b/Lib9c/Model/Collection/NonFungibleCollectionMaterial.cs new file mode 100644 index 0000000000..709415b875 --- /dev/null +++ b/Lib9c/Model/Collection/NonFungibleCollectionMaterial.cs @@ -0,0 +1,45 @@ +using System; +using Bencodex.Types; +using Nekoyume.Model.Item; +using Nekoyume.Model.State; + +namespace Nekoyume.Model.Collection +{ + public class NonFungibleCollectionMaterial : ICollectionMaterial + { + public MaterialType Type => MaterialType.NonFungible; + public int ItemId { get; set; } + public int ItemCount { get; set; } + public Guid NonFungibleId { get; set; } + public int Level { get; set; } + public int OptionCount { get; set; } + public bool SkillContains { get; set; } + + public IValue Serialize() + { + return List.Empty + .Add((int)Type) + .Add(ItemId) + .Add(ItemCount) + .Add(NonFungibleId.Serialize()) + .Add(Level) + .Add(OptionCount) + .Add(SkillContains.Serialize()); + } + + public NonFungibleCollectionMaterial(List serialized) + { + ItemId = (Integer)serialized[1]; + ItemCount = (Integer)serialized[2]; + NonFungibleId = serialized[3].ToGuid(); + Level = (Integer)serialized[4]; + OptionCount = (Integer)serialized[5]; + SkillContains = serialized[6].ToBoolean(); + } + + public NonFungibleCollectionMaterial() + { + ItemCount = 1; + } + } +} diff --git a/Lib9c/Model/Item/Inventory.cs b/Lib9c/Model/Item/Inventory.cs index 816179db08..4d5e0387bf 100644 --- a/Lib9c/Model/Item/Inventory.cs +++ b/Lib9c/Model/Item/Inventory.cs @@ -415,6 +415,59 @@ e.item is ITradableFungibleItem tradableFungibleItem && return true; } + + public bool RemoveMaterial(int id, int count = 1) + { + if (count <= 0) + { + return false; + } + + List targetItems = new List(); + foreach (var item in _items) + { + if (item.Locked) + { + continue; + } + if (item.item is Material material && material.Id == id) + { + targetItems.Add(item); + } + } + + targetItems = targetItems + .OrderBy(e => e.count) + .ToList(); + + if (!targetItems.Any()) + { + return false; + } + + var totalCount = targetItems.Sum(e => e.count); + if (totalCount < count) + { + return false; + } + + for (var i = 0; i < targetItems.Count; i++) + { + var item = targetItems[i]; + if (item.count > count) + { + item.count -= count; + break; + } + + count -= item.count; + item.count = 0; + _items.Remove(item); + } + + return true; + } + [Obsolete("Use RemoveFungibleItem")] public bool RemoveFungibleItem2( IFungibleItem fungibleItem, diff --git a/Lib9c/TableCSV/CollectionSheet.csv b/Lib9c/TableCSV/CollectionSheet.csv index b72b50d9e8..99657dd6eb 100644 --- a/Lib9c/TableCSV/CollectionSheet.csv +++ b/Lib9c/TableCSV/CollectionSheet.csv @@ -1,3 +1,3 @@ -id,item_id,count,level,option_count,skill,item_id,count,level,option_count,skillitem_id,count,level,option_count,skillitem_id,count,level,option_count,skillitem_id,count,level,option_count,skill,item_id,count,level,option_count,skill,stat_type,modify_type,modify_value,stat_type,modify_type,modify_value,stat_type,modify_type,modify_value -1,10110000,1,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,ATK,Add,1,,,,,, +id,item_id,count,level,option_count,skill,item_id,count,level,option_count,skill,item_id,count,level,option_count,skill,item_id,count,level,option_count,skill,item_id,count,level,option_count,skill,item_id,count,level,option_count,skill,stat_type,modify_type,modify_value,stat_type,modify_type,modify_value,stat_type,modify_type,modify_value +1,10110000,1,0,,,302000,2,,,,,,,,,,,,,,,,,,,,,,,,ATK,Add,1,,,,,, 2,10110000,1,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,1,,,,,, diff --git a/Lib9c/TableData/CollectionSheet.cs b/Lib9c/TableData/CollectionSheet.cs index 6b7b78b538..b0f517f1b4 100644 --- a/Lib9c/TableData/CollectionSheet.cs +++ b/Lib9c/TableData/CollectionSheet.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Linq; +using Nekoyume.Model.Item; using Nekoyume.Model.Stat; using static Nekoyume.TableData.TableExtensions; @@ -15,6 +17,21 @@ public class CollectionMaterial public int Level; public int OptionCount; public bool SkillContains; + + public bool Validate(ItemUsable itemUsable) + { + switch (itemUsable) + { + case Equipment equipment: + return equipment.Id == ItemId && equipment.level == Level && + equipment.GetOptionCount() == OptionCount && + (equipment.Skills.Any() == SkillContains || equipment.BuffSkills.Any() == SkillContains); + case Consumable consumable: + return consumable.Id == ItemId; + default: + return false; + } + } } public class Row : SheetRow @@ -40,7 +57,7 @@ public override void Set(IReadOnlyList fields) { ItemId = itemId, Count = ParseInt(fields[2 + offset]), - Level = ParseInt(fields[3 + offset]), + Level = ParseInt(fields[3 + offset], 0), OptionCount = ParseInt(fields[4 + offset], 0), SkillContains = ParseBool(fields[5 + offset], false) }); From bbc9c2c4887cd65d58ee1faab4be6bf3e6674fd6 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Fri, 2 Feb 2024 16:49:44 +0900 Subject: [PATCH 11/64] Migration libplanet 4.0 --- .Lib9c.Tests/Action/ActivateCollectionTest.cs | 25 ++++----- .../Model/State/CollectionStateTest.cs | 2 +- Lib9c/Action/ActivateCollection.cs | 30 +++++++---- Lib9c/Model/State/CollectionState.cs | 2 +- Lib9c/Module/CollectionModule.cs | 51 +++++++++++++++++++ 5 files changed, 85 insertions(+), 25 deletions(-) create mode 100644 Lib9c/Module/CollectionModule.cs diff --git a/.Lib9c.Tests/Action/ActivateCollectionTest.cs b/.Lib9c.Tests/Action/ActivateCollectionTest.cs index d7e0e31f81..40fe01c9fd 100644 --- a/.Lib9c.Tests/Action/ActivateCollectionTest.cs +++ b/.Lib9c.Tests/Action/ActivateCollectionTest.cs @@ -12,13 +12,14 @@ namespace Lib9c.Tests.Action using Nekoyume.Model.Collection; using Nekoyume.Model.Item; using Nekoyume.Model.State; + using Nekoyume.Module; using Nekoyume.TableData; using Xunit; using static SerializeKeys; public class ActivateCollectionTest { - private readonly IAccount _initialState; + private readonly IWorld _initialState; private readonly Address _agentAddress; private readonly Address _avatarAddress; private readonly TableSheets _tableSheets; @@ -50,18 +51,15 @@ public ActivateCollectionTest() var questListAddress = _avatarAddress.Derive(LegacyQuestListKey); agentState.avatarAddresses.Add(0, _avatarAddress); - _initialState = new Account(MockState.Empty) - .SetState(_agentAddress, agentState.SerializeV2()) - .SetState(_avatarAddress, avatarState.SerializeV2()) - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(worldInformationAddress, avatarState.worldInformation.Serialize()) - .SetState(questListAddress, avatarState.questList.Serialize()) - .SetState(gameConfigState.address, gameConfigState.Serialize()); + _initialState = new World(new MockWorldState()) + .SetAgentState(_agentAddress, agentState) + .SetAvatarState(_avatarAddress, avatarState, true, true, true, true) + .SetLegacyState(gameConfigState.address, gameConfigState.Serialize()); foreach (var (key, value) in sheets) { _initialState = _initialState - .SetState(Addresses.TableSheet.Derive(key), value.Serialize()); + .SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); } } @@ -69,7 +67,7 @@ public ActivateCollectionTest() public void Execute() { var row = _tableSheets.CollectionSheet.Values.First(); - var avatarState = _initialState.GetAvatarStateV2(_avatarAddress); + var avatarState = _initialState.GetAvatarState(_avatarAddress); var materials = new List(); foreach (var material in row.Materials) { @@ -97,7 +95,7 @@ public void Execute() } var inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - var state = _initialState.SetState(inventoryAddress, avatarState.inventory.Serialize()); + var state = _initialState.SetAvatarState(_avatarAddress, avatarState, false, true, false, false); IActionContext context = new ActionContext() { PreviousState = state, @@ -112,11 +110,10 @@ public void Execute() var nextState = activateCollection.Execute(context); var collectionAddress = CollectionState.Derive(_avatarAddress); - var rawList = Assert.IsType(nextState.GetState(collectionAddress)); - var collectionState = new CollectionState(rawList); + var collectionState = nextState.GetCollectionState(collectionAddress); Assert.Equal(row.Id, collectionState.Ids.Single()); - var nextAvatarState = nextState.GetAvatarStateV2(_avatarAddress); + var nextAvatarState = nextState.GetAvatarState(_avatarAddress); Assert.Empty(nextAvatarState.inventory.Items); } } diff --git a/.Lib9c.Tests/Model/State/CollectionStateTest.cs b/.Lib9c.Tests/Model/State/CollectionStateTest.cs index e4cd03ac95..56efdaf3f1 100644 --- a/.Lib9c.Tests/Model/State/CollectionStateTest.cs +++ b/.Lib9c.Tests/Model/State/CollectionStateTest.cs @@ -30,7 +30,7 @@ public void Serialize() }, }; - var serialized = (List)state.Serialize(); + var serialized = (List)state.SerializeList(); var expected = List.Empty .Add(address.Serialize()) .Add(List.Empty.Add(1).Add(2)); diff --git a/Lib9c/Action/ActivateCollection.cs b/Lib9c/Action/ActivateCollection.cs index 4532b6f27b..a7c1ea59f0 100644 --- a/Lib9c/Action/ActivateCollection.cs +++ b/Lib9c/Action/ActivateCollection.cs @@ -10,6 +10,7 @@ using Nekoyume.Model.Collection; using Nekoyume.Model.Item; using Nekoyume.Model.State; +using Nekoyume.Module; using Nekoyume.TableData; using static Lib9c.SerializeKeys; @@ -21,12 +22,11 @@ public class ActivateCollection: GameAction public Address AvatarAddress; public int CollectionId; public List Materials = new(); - public override IAccount Execute(IActionContext context) + public override IWorld Execute(IActionContext context) { context.UseGas(1); var states = context.PreviousState; - if (states.TryGetAvatarStateV2(context.Signer, AvatarAddress, out var avatarState, - out _)) + if (states.TryGetAvatarState(context.Signer, AvatarAddress, out var avatarState)) { var sheets = states.GetSheets(containItemSheet: true, sheetTypes: new[] { @@ -77,17 +77,29 @@ public override IAccount Execute(IActionContext context) } var collectionAddress = CollectionState.Derive(AvatarAddress); - var collectionState = states.TryGetState(collectionAddress, out List rawState) - ? new CollectionState(rawState) - : new CollectionState + CollectionState collectionState; + try + { + collectionState = states.GetCollectionState(collectionAddress); + } + catch (FailedLoadStateException) + { + collectionState = new CollectionState { Address = collectionAddress }; + } + catch (InvalidCastException) + { + collectionState = new CollectionState + { + Address = collectionAddress + }; + } collectionState.Ids.Add(CollectionId); - var inventoryAddress = AvatarAddress.Derive(LegacyInventoryKey); return states - .SetState(inventoryAddress, avatarState.inventory.Serialize()) - .SetState(collectionAddress, collectionState.Serialize()); + .SetAvatarState(AvatarAddress, avatarState, false, true, false, false) + .SetCollectionState(collectionAddress, collectionState); } throw new FailedLoadStateException(AvatarAddress, typeof(Dictionary)); diff --git a/Lib9c/Model/State/CollectionState.cs b/Lib9c/Model/State/CollectionState.cs index a0d9663267..ed3604c1d6 100644 --- a/Lib9c/Model/State/CollectionState.cs +++ b/Lib9c/Model/State/CollectionState.cs @@ -30,7 +30,7 @@ public CollectionState(List serialized) } } - public IValue Serialize() + public IValue SerializeList() { return List.Empty .Add(Address.Serialize()) diff --git a/Lib9c/Module/CollectionModule.cs b/Lib9c/Module/CollectionModule.cs new file mode 100644 index 0000000000..fca064d5c2 --- /dev/null +++ b/Lib9c/Module/CollectionModule.cs @@ -0,0 +1,51 @@ +using System; +using Bencodex.Types; +using Libplanet.Action.State; +using Libplanet.Crypto; +using Nekoyume.Action; +using Nekoyume.Model.State; +using Serilog; + +namespace Nekoyume.Module +{ + public static class CollectionModule + { + public static CollectionState GetCollectionState(this IWorldState worldState, Address address) + { + var serializedCollection = worldState.GetResolvedState(address, Addresses.Avatar); + if (serializedCollection is null) + { + var msg = $"No Collection state ({address.ToHex()})"; + throw new FailedLoadStateException(msg); + } + + try + { + if (serializedCollection is List list) + { + return new CollectionState(list); + } + + throw new InvalidCastException( + "Serialized Collection state must be a list."); + } + catch (InvalidCastException e) + { + Log.Error( + e, + "Invalid Collection state ({0}): {1}", + address.ToHex(), + serializedCollection + ); + throw; + } + } + + public static IWorld SetCollectionState(this IWorld world, Address collection, CollectionState state) + { + var account = world.GetAccount(Addresses.Avatar); + account = account.SetState(collection, state.SerializeList()); + return world.SetAccount(Addresses.Avatar, account); + } + } +} From 14721901f497538138830a079b87bdab68ca6682 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Fri, 2 Feb 2024 17:53:18 +0900 Subject: [PATCH 12/64] Apply review suggestion - Remove address derive and use Account.Collection address - Remove address serialize --- .Lib9c.Tests/Action/ActivateCollectionTest.cs | 10 +--------- .Lib9c.Tests/Model/State/CollectionStateTest.cs | 11 ----------- Lib9c/Action/ActivateCollection.cs | 15 ++++----------- Lib9c/Addresses.cs | 1 + Lib9c/Model/State/CollectionState.cs | 9 +-------- Lib9c/Module/CollectionModule.cs | 6 +++--- 6 files changed, 10 insertions(+), 42 deletions(-) diff --git a/.Lib9c.Tests/Action/ActivateCollectionTest.cs b/.Lib9c.Tests/Action/ActivateCollectionTest.cs index 40fe01c9fd..2983ac1e73 100644 --- a/.Lib9c.Tests/Action/ActivateCollectionTest.cs +++ b/.Lib9c.Tests/Action/ActivateCollectionTest.cs @@ -1,9 +1,7 @@ namespace Lib9c.Tests.Action { - using System; using System.Collections.Generic; using System.Linq; - using Bencodex.Types; using Libplanet.Action; using Libplanet.Action.State; using Libplanet.Crypto; @@ -15,7 +13,6 @@ namespace Lib9c.Tests.Action using Nekoyume.Module; using Nekoyume.TableData; using Xunit; - using static SerializeKeys; public class ActivateCollectionTest { @@ -46,9 +43,6 @@ public ActivateCollectionTest() { level = 100, }; - var inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = _avatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = _avatarAddress.Derive(LegacyQuestListKey); agentState.avatarAddresses.Add(0, _avatarAddress); _initialState = new World(new MockWorldState()) @@ -94,7 +88,6 @@ public void Execute() } } - var inventoryAddress = _avatarAddress.Derive(LegacyInventoryKey); var state = _initialState.SetAvatarState(_avatarAddress, avatarState, false, true, false, false); IActionContext context = new ActionContext() { @@ -109,8 +102,7 @@ public void Execute() }; var nextState = activateCollection.Execute(context); - var collectionAddress = CollectionState.Derive(_avatarAddress); - var collectionState = nextState.GetCollectionState(collectionAddress); + var collectionState = nextState.GetCollectionState(_avatarAddress); Assert.Equal(row.Id, collectionState.Ids.Single()); var nextAvatarState = nextState.GetAvatarState(_avatarAddress); diff --git a/.Lib9c.Tests/Model/State/CollectionStateTest.cs b/.Lib9c.Tests/Model/State/CollectionStateTest.cs index 56efdaf3f1..a1553a412f 100644 --- a/.Lib9c.Tests/Model/State/CollectionStateTest.cs +++ b/.Lib9c.Tests/Model/State/CollectionStateTest.cs @@ -9,20 +9,11 @@ namespace Lib9c.Tests.Model.State public class CollectionStateTest { - [Fact] - public void Derive() - { - var address = new PrivateKey().Address; - Assert.Equal(address.Derive(nameof(CollectionState)), CollectionState.Derive(address)); - } - [Fact] public void Serialize() { - var address = new PrivateKey().Address; var state = new CollectionState { - Address = address, Ids = new List { 1, @@ -32,12 +23,10 @@ public void Serialize() var serialized = (List)state.SerializeList(); var expected = List.Empty - .Add(address.Serialize()) .Add(List.Empty.Add(1).Add(2)); Assert.Equal(expected, serialized); var deserialized = new CollectionState(serialized); - Assert.Equal(state.Address, deserialized.Address); Assert.Equal(state.Ids, deserialized.Ids); } } diff --git a/Lib9c/Action/ActivateCollection.cs b/Lib9c/Action/ActivateCollection.cs index a7c1ea59f0..42fc8e1c95 100644 --- a/Lib9c/Action/ActivateCollection.cs +++ b/Lib9c/Action/ActivateCollection.cs @@ -76,30 +76,23 @@ public override IWorld Execute(IActionContext context) throw new Exception(); } - var collectionAddress = CollectionState.Derive(AvatarAddress); CollectionState collectionState; try { - collectionState = states.GetCollectionState(collectionAddress); + collectionState = states.GetCollectionState(AvatarAddress); } catch (FailedLoadStateException) { - collectionState = new CollectionState - { - Address = collectionAddress - }; + collectionState = new CollectionState(); } catch (InvalidCastException) { - collectionState = new CollectionState - { - Address = collectionAddress - }; + collectionState = new CollectionState(); } collectionState.Ids.Add(CollectionId); return states .SetAvatarState(AvatarAddress, avatarState, false, true, false, false) - .SetCollectionState(collectionAddress, collectionState); + .SetCollectionState(AvatarAddress, collectionState); } throw new FailedLoadStateException(AvatarAddress, typeof(Dictionary)); diff --git a/Lib9c/Addresses.cs b/Lib9c/Addresses.cs index c77fadb6a5..5b0908085d 100644 --- a/Lib9c/Addresses.cs +++ b/Lib9c/Addresses.cs @@ -44,6 +44,7 @@ public static class Addresses public static readonly Address Inventory = new Address("000000000000000000000000000000000000001c"); public static readonly Address WorldInformation = new Address("000000000000000000000000000000000000001d"); public static readonly Address QuestList = new Address("000000000000000000000000000000000000001e"); + public static readonly Address Collection = new Address("000000000000000000000000000000000000001f"); public static Address GetSheetAddress() where T : ISheet => GetSheetAddress(typeof(T).Name); diff --git a/Lib9c/Model/State/CollectionState.cs b/Lib9c/Model/State/CollectionState.cs index ed3604c1d6..7f7e080924 100644 --- a/Lib9c/Model/State/CollectionState.cs +++ b/Lib9c/Model/State/CollectionState.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using Bencodex.Types; using Libplanet.Crypto; -using Nekoyume.Action; namespace Nekoyume.Model.State { @@ -10,10 +9,6 @@ namespace Nekoyume.Model.State /// public class CollectionState { - public static Address Derive(Address avatarAddress) => - avatarAddress.Derive(nameof(CollectionState)); - - public Address Address; public List Ids = new(); public CollectionState() @@ -22,8 +17,7 @@ public CollectionState() public CollectionState(List serialized) { - Address = serialized[0].ToAddress(); - var rawList = (List) serialized[1]; + var rawList = (List) serialized[0]; foreach (var value in rawList) { Ids.Add((Integer)value); @@ -33,7 +27,6 @@ public CollectionState(List serialized) public IValue SerializeList() { return List.Empty - .Add(Address.Serialize()) .Add(new List(Ids)); } } diff --git a/Lib9c/Module/CollectionModule.cs b/Lib9c/Module/CollectionModule.cs index fca064d5c2..a09383962a 100644 --- a/Lib9c/Module/CollectionModule.cs +++ b/Lib9c/Module/CollectionModule.cs @@ -12,7 +12,7 @@ public static class CollectionModule { public static CollectionState GetCollectionState(this IWorldState worldState, Address address) { - var serializedCollection = worldState.GetResolvedState(address, Addresses.Avatar); + var serializedCollection = worldState.GetResolvedState(address, Addresses.Collection); if (serializedCollection is null) { var msg = $"No Collection state ({address.ToHex()})"; @@ -43,9 +43,9 @@ public static CollectionState GetCollectionState(this IWorldState worldState, Ad public static IWorld SetCollectionState(this IWorld world, Address collection, CollectionState state) { - var account = world.GetAccount(Addresses.Avatar); + var account = world.GetAccount(Addresses.Collection); account = account.SetState(collection, state.SerializeList()); - return world.SetAccount(Addresses.Avatar, account); + return world.SetAccount(Addresses.Collection, account); } } } From dbf796b83060b7d95e05a8861108084788989561 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Fri, 2 Feb 2024 18:33:50 +0900 Subject: [PATCH 13/64] Use Bencoded instead of serialize --- .Lib9c.Tests/Model/State/CollectionStateTest.cs | 4 ++-- Lib9c/Model/State/CollectionState.cs | 10 +++++----- Lib9c/Module/CollectionModule.cs | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.Lib9c.Tests/Model/State/CollectionStateTest.cs b/.Lib9c.Tests/Model/State/CollectionStateTest.cs index a1553a412f..b02dc0cf4b 100644 --- a/.Lib9c.Tests/Model/State/CollectionStateTest.cs +++ b/.Lib9c.Tests/Model/State/CollectionStateTest.cs @@ -10,7 +10,7 @@ namespace Lib9c.Tests.Model.State public class CollectionStateTest { [Fact] - public void Serialize() + public void Bencoded() { var state = new CollectionState { @@ -21,7 +21,7 @@ public void Serialize() }, }; - var serialized = (List)state.SerializeList(); + var serialized = state.Bencoded; var expected = List.Empty .Add(List.Empty.Add(1).Add(2)); Assert.Equal(expected, serialized); diff --git a/Lib9c/Model/State/CollectionState.cs b/Lib9c/Model/State/CollectionState.cs index 7f7e080924..b62af1ab83 100644 --- a/Lib9c/Model/State/CollectionState.cs +++ b/Lib9c/Model/State/CollectionState.cs @@ -1,13 +1,13 @@ using System.Collections.Generic; +using Bencodex; using Bencodex.Types; -using Libplanet.Crypto; namespace Nekoyume.Model.State { /// /// Represents the state of a collection. /// - public class CollectionState + public class CollectionState : IBencodable { public List Ids = new(); @@ -24,10 +24,10 @@ public CollectionState(List serialized) } } - public IValue SerializeList() + public CollectionState(IValue bencoded) : this((List)bencoded) { - return List.Empty - .Add(new List(Ids)); } + + public IValue Bencoded => List.Empty.Add(new List(Ids)); } } diff --git a/Lib9c/Module/CollectionModule.cs b/Lib9c/Module/CollectionModule.cs index a09383962a..9ba59ac85b 100644 --- a/Lib9c/Module/CollectionModule.cs +++ b/Lib9c/Module/CollectionModule.cs @@ -44,7 +44,7 @@ public static CollectionState GetCollectionState(this IWorldState worldState, Ad public static IWorld SetCollectionState(this IWorld world, Address collection, CollectionState state) { var account = world.GetAccount(Addresses.Collection); - account = account.SetState(collection, state.SerializeList()); + account = account.SetState(collection, state.Bencoded); return world.SetAccount(Addresses.Collection, account); } } From c847e9c7698facbdfd7eae26aee50b53301f85cd Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Sat, 3 Feb 2024 10:43:41 +0900 Subject: [PATCH 14/64] Introduce CollectionStats --- .Lib9c.Tests/Model/PlayerTest.cs | 27 ++++++++++++++++ Lib9c/Model/Stat/CharacterStats.cs | 50 +++++++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/.Lib9c.Tests/Model/PlayerTest.cs b/.Lib9c.Tests/Model/PlayerTest.cs index 8458cb520e..10df5aac06 100644 --- a/.Lib9c.Tests/Model/PlayerTest.cs +++ b/.Lib9c.Tests/Model/PlayerTest.cs @@ -664,5 +664,32 @@ public void Vampiric(int duration, int percent) Assert.Contains(logList, e => e is RemoveBuffs); Assert.Contains(logList, e => e is Tick); } + + [Fact] + public void SetCollectionStatsTest() + { + var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Stat.StatType == StatType.HP); + var costume = (Equipment)ItemFactory.CreateItem(_tableSheets.ItemSheet[row.Id], new TestRandom()); + costume.equipped = true; + _avatarState.inventory.AddItem(costume); + + var player = new Player( + _avatarState, + _tableSheets.CharacterSheet, + _tableSheets.CharacterLevelSheet, + _tableSheets.EquipmentItemSetEffectSheet + ); + + Assert.Equal(row.Stat.BaseValue, player.Stats.EquipmentStats.HP); + Assert.Equal(player.HP, player.Stats.BaseHP + row.Stat.BaseValue); + + var modifiers = new List(); + var addModifier = new StatModifier(StatType.HP, StatModifier.OperationType.Add, 100); + modifiers.Add(new StatModifier(StatType.HP, StatModifier.OperationType.Percentage, 100)); + modifiers.Add(new StatModifier(StatType.HP, StatModifier.OperationType.Percentage, -100)); + modifiers.Add(addModifier); + player.Stats.SetCollections(modifiers); + Assert.Equal(player.HP, player.Stats.BaseHP + row.Stat.BaseValue + addModifier.Value); + } } } diff --git a/Lib9c/Model/Stat/CharacterStats.cs b/Lib9c/Model/Stat/CharacterStats.cs index e8272da61d..1bfcfaf35b 100644 --- a/Lib9c/Model/Stat/CharacterStats.cs +++ b/Lib9c/Model/Stat/CharacterStats.cs @@ -25,6 +25,7 @@ public class CharacterStats : Stats, IBaseAndAdditionalStats, ICloneable private readonly Stats _runeStats = new Stats(); private readonly Stats _buffStats = new Stats(); private readonly Stats _optionalStats = new Stats(); + private readonly Stats _collectionStats = new Stats(); private readonly List _initialStatModifiers = new List(); private readonly List _equipmentStatModifiers = new List(); @@ -32,6 +33,7 @@ public class CharacterStats : Stats, IBaseAndAdditionalStats, ICloneable private readonly List _runeStatModifiers = new List(); private readonly Dictionary _buffStatModifiers = new Dictionary(); private readonly List _optionalStatModifiers = new List(); + private readonly List _collectionStatModifiers = new List(); public readonly StatMap StatWithItems = new StatMap(); @@ -43,6 +45,7 @@ public class CharacterStats : Stats, IBaseAndAdditionalStats, ICloneable public IStats RuneStats => _runeStats; public IStats BuffStats => _buffStats; public IStats OptionalStats => _optionalStats; + public IStats CollectionStats => _collectionStats; public long BaseHP => BaseStats.HP; public long BaseATK => BaseStats.ATK; @@ -298,6 +301,45 @@ public CharacterStats SetBuffs(IEnumerable value, bool updateImme return this; } + public CharacterStats SetCollections(IEnumerable statModifiers, + bool updateImmediate = true) + { + _collectionStatModifiers.Clear(); + var perModifiers = new List(); + foreach (var modifier in statModifiers) + { + switch (modifier.Operation) + { + case StatModifier.OperationType.Add: + _collectionStatModifiers.Add(modifier); + break; + case StatModifier.OperationType.Percentage: + perModifiers.Add(modifier); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + var groupBy = perModifiers.GroupBy(m => m.StatType).ToList(); + foreach (var group in groupBy) + { + var statType = group.Key; + var sum = group.Sum(g => g.Value); + if (sum > 0L) + { + _collectionStatModifiers.Add(new StatModifier(statType, StatModifier.OperationType.Percentage, sum)); + } + } + + if (updateImmediate) + { + UpdateEquipmentStats(); + } + + return this; + } + public void AddBuff(Buff.StatBuff buff, bool updateImmediate = true) { _buffStatModifiers[buff.RowData.GroupId] = buff.GetModifier(); @@ -417,12 +459,18 @@ private void UpdateBuffStats() private void UpdateOptionalStats() { _optionalStats.Set(_optionalStatModifiers, _baseStats, _equipmentStats, _consumableStats, _runeStats, _buffStats); + UpdateCollectionStats(); + } + + private void UpdateCollectionStats() + { + _collectionStats.Set(_collectionStatModifiers, _baseStats, _equipmentStats, _collectionStats, _runeStats, _buffStats, _optionalStats); UpdateTotalStats(); } private void UpdateTotalStats() { - Set(_statMap, _baseStats, _equipmentStats, _consumableStats, _runeStats, _buffStats, _optionalStats); + Set(_statMap, _baseStats, _equipmentStats, _consumableStats, _runeStats, _buffStats, _optionalStats, _collectionStats); foreach (var stat in _statMap.GetDecimalStats(false)) { From 8bdd4014e584e3813983cba1d9b033b8728b5119 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Sat, 3 Feb 2024 14:53:20 +0900 Subject: [PATCH 15/64] Add collection module interface --- .Lib9c.Tests/Module/CollectionModuleTest.cs | 72 +++++++++++++++++++++ Lib9c/Module/CollectionModule.cs | 65 +++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 .Lib9c.Tests/Module/CollectionModuleTest.cs diff --git a/.Lib9c.Tests/Module/CollectionModuleTest.cs b/.Lib9c.Tests/Module/CollectionModuleTest.cs new file mode 100644 index 0000000000..984988bf99 --- /dev/null +++ b/.Lib9c.Tests/Module/CollectionModuleTest.cs @@ -0,0 +1,72 @@ +namespace Lib9c.Tests.Module +{ + using System.Collections.Generic; + using Lib9c.Tests.Action; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Nekoyume.Action; + using Nekoyume.Model.State; + using Nekoyume.Module; + using Xunit; + + public class CollectionModuleTest + { + [Fact] + public void CollectionState() + { + IWorld states = new World(new MockWorldState()); + var address = new PrivateKey().Address; + Assert.Throws(() => states.GetCollectionState(address)); + Assert.False(states.TryGetCollectionState(address, out _)); + + var state = new CollectionState + { + Ids = new List + { + 1, + }, + }; + states = states.SetCollectionState(address, state); + var result = states.GetCollectionState(address); + Assert.Equal(state.Ids, result.Ids); + + Assert.True(states.TryGetCollectionState(address, out var result2)); + Assert.Equal(state.Ids, result2.Ids); + } + + [Fact] + public void CollectionStates() + { + IWorld states = new World(new MockWorldState()); + var address = new PrivateKey().Address; + var address2 = new PrivateKey().Address; + var addresses = new[] { address, address2 }; + var result = states.GetCollectionStates(addresses); + Assert.Equal(addresses.Length, result.Count); + Assert.All(result, Assert.Null); + + var state = new CollectionState + { + Ids = new List + { + 1, + }, + }; + states = states.SetCollectionState(address, state); + result = states.GetCollectionStates(addresses); + for (int i = 0; i < addresses.Length; i++) + { + switch (i) + { + case 0: + Assert.NotNull(result[i]); + Assert.Equal(state.Ids, result[i].Ids); + break; + case 1: + Assert.Null(result[i]); + break; + } + } + } + } +} diff --git a/Lib9c/Module/CollectionModule.cs b/Lib9c/Module/CollectionModule.cs index 9ba59ac85b..cde5fd0030 100644 --- a/Lib9c/Module/CollectionModule.cs +++ b/Lib9c/Module/CollectionModule.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Bencodex.Types; using Libplanet.Action.State; using Libplanet.Crypto; @@ -8,8 +9,19 @@ namespace Nekoyume.Module { + /// + /// Provides utility methods for working with collection states in the world state. + /// public static class CollectionModule { + /// + /// Get the CollectionState for a given address from the world state. + /// + /// The world state object. + /// The address to retrieve the CollectionState. + /// The CollectionState corresponding to the address. + /// Thrown when the Collection state for the given address is not found in the world state. + /// Thrown when the serialized Collection state is not in the correct format. public static CollectionState GetCollectionState(this IWorldState worldState, Address address) { var serializedCollection = worldState.GetResolvedState(address, Addresses.Collection); @@ -41,11 +53,64 @@ public static CollectionState GetCollectionState(this IWorldState worldState, Ad } } + /// + /// Sets the state of a collection in the world. + /// + /// The address of the collection. + /// The state of the collection. + /// The updated world with the updated collection state. public static IWorld SetCollectionState(this IWorld world, Address collection, CollectionState state) { var account = world.GetAccount(Addresses.Collection); account = account.SetState(collection, state.Bencoded); return world.SetAccount(Addresses.Collection, account); } + + /// + /// Tries to get the collection state for a specific address from the given world state. + /// + /// The world state from which to get the collection state. + /// The address for which to retrieve the collection state. + /// The resulting collection state, if found. + /// True if the collection state is found, otherwise false. + public static bool TryGetCollectionState(this IWorldState worldState, Address address, out CollectionState collectionState) + { + try + { + collectionState = GetCollectionState(worldState, address); + return true; + } + catch (Exception) + { + collectionState = null; + return false; + } + } + + /// + /// Retrieves the collection states for the given addresses from the world state. + /// + /// The world state used to retrieve the collection states. + /// The list of addresses to retrieve the collection states for. + /// A list of CollectionState objects representing the collection states for the given addresses, + /// or null for addresses that do not have a collection state. + public static List GetCollectionStates(this IWorldState worldState, + IReadOnlyList
addresses) + { + var result = new List(); + foreach (var serialized in worldState.GetAccountState(Addresses.Collection).GetStates(addresses)) + { + if (serialized is List bencoded) + { + result.Add(new CollectionState(bencoded)); + } + else + { + result.Add(null); + } + } + + return result; + } } } From 9e965a292a5d28968a1fe20eb2f10f6439bb550a Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Sat, 3 Feb 2024 14:55:45 +0900 Subject: [PATCH 16/64] Calculate collection stats in actions --- .../Action/Scenario/AuraScenarioTest.cs | 4 +- .../Action/Scenario/CollectionScenarioTest.cs | 230 ++++++++++++++++++ .Lib9c.Tests/Model/ArenaSimulatorTest.cs | 23 +- .Lib9c.Tests/Model/BattleLogTest.cs | 4 +- .Lib9c.Tests/Model/PlayerTest.cs | 27 +- .Lib9c.Tests/Model/RaidSimulatorV3Test.cs | 9 +- .Lib9c.Tests/Model/Skill/NormalAttackTest.cs | 1 + .Lib9c.Tests/Model/StageSimulatorTest.cs | 8 +- Lib9c/Action/BattleArena.cs | 54 +++- Lib9c/Action/EventDungeonBattle.cs | 4 +- Lib9c/Action/HackAndSlash.cs | 65 +++-- Lib9c/Action/Raid.cs | 30 ++- Lib9c/Arena/ArenaSimulator.cs | 11 +- Lib9c/Battle/RaidSimulator.cs | 6 +- Lib9c/Battle/StageSimulator.cs | 3 + 15 files changed, 418 insertions(+), 61 deletions(-) create mode 100644 .Lib9c.Tests/Action/Scenario/CollectionScenarioTest.cs diff --git a/.Lib9c.Tests/Action/Scenario/AuraScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/AuraScenarioTest.cs index 8c2f7a6e8f..d9fd8fd6a4 100644 --- a/.Lib9c.Tests/Action/Scenario/AuraScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/AuraScenarioTest.cs @@ -261,7 +261,9 @@ public void Arena() var log = simulator.Simulate( myArenaPlayerDigest, enemyArenaPlayerDigest, - _tableSheets.GetArenaSimulatorSheets()); + _tableSheets.GetArenaSimulatorSheets(), + new List(), + new List()); // Check player, enemy equip aura foreach (var spawn in log.OfType()) { diff --git a/.Lib9c.Tests/Action/Scenario/CollectionScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/CollectionScenarioTest.cs new file mode 100644 index 0000000000..74c4e7d4fd --- /dev/null +++ b/.Lib9c.Tests/Action/Scenario/CollectionScenarioTest.cs @@ -0,0 +1,230 @@ +namespace Lib9c.Tests.Action.Scenario +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Libplanet.Action.State; + using Libplanet.Crypto; + using Libplanet.Types.Assets; + using Nekoyume; + using Nekoyume.Action; + using Nekoyume.Model.EnumType; + using Nekoyume.Model.State; + using Nekoyume.Module; + using Nekoyume.TableData; + using Xunit; + + public class CollectionScenarioTest + { + private readonly Address _agentAddress; + private readonly Address _avatarAddress; + private readonly Address _enemyAvatarAddress; + private readonly IWorld _initialState; + private readonly TableSheets _tableSheets; + private readonly Dictionary _sheets; + + public CollectionScenarioTest() + { + _agentAddress = new PrivateKey().Address; + var agentState = new AgentState(_agentAddress); + _avatarAddress = new PrivateKey().Address; + _enemyAvatarAddress = new PrivateKey().Address; + var rankingMapAddress = _avatarAddress.Derive("ranking_map"); + _sheets = TableSheetsImporter.ImportSheets(); + _tableSheets = new TableSheets(_sheets); + var gameConfigState = new GameConfigState(_sheets[nameof(GameConfigSheet)]); + var addresses = new[] { _avatarAddress, _enemyAvatarAddress }; + _initialState = new World(new MockWorldState()); + for (int i = 0; i < addresses.Length; i++) + { + var avatarAddress = addresses[i]; + agentState.avatarAddresses.Add(i, avatarAddress); + var avatarState = new AvatarState( + _avatarAddress, + _agentAddress, + 0, + _tableSheets.GetAvatarSheets(), + gameConfigState, + rankingMapAddress + ); + _initialState = _initialState.SetAvatarState( + avatarAddress, avatarState, true, true, true, true); + } + + var currency = Currency.Legacy("NCG", 2, minters: null); + _initialState = _initialState + .SetAgentState(_agentAddress, agentState) + .SetLegacyState( + Addresses.GoldCurrency, + new GoldCurrencyState(currency).Serialize()) + .SetLegacyState(gameConfigState.address, gameConfigState.Serialize()) + .MintAsset(new ActionContext(), _agentAddress, Currencies.Crystal * 2); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void HackAndSlash(bool collectionExist) + { + var states = _initialState; + if (collectionExist) + { + var collectionState = new CollectionState(); + collectionState.Ids.Add(1); + states = states.SetCollectionState(_avatarAddress, collectionState); + } + + foreach (var (key, value) in _sheets) + { + if (key == nameof(CollectionSheet) && !collectionExist) + { + continue; + } + + states = states + .SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); + } + + var action = new HackAndSlash + { + AvatarAddress = _avatarAddress, + Equipments = new List(), + Costumes = new List(), + StageId = 1, + WorldId = 1, + RuneInfos = new List(), + Foods = new List(), + }; + + action.Execute(new ActionContext + { + PreviousState = states, + Signer = _agentAddress, + RandomSeed = 0, + }); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void Raid(bool collectionExist) + { + var states = _initialState; + if (collectionExist) + { + var collectionState = new CollectionState(); + collectionState.Ids.Add(1); + states = states.SetCollectionState(_avatarAddress, collectionState); + } + + foreach (var (key, value) in _sheets) + { + if (key == nameof(CollectionSheet) && !collectionExist) + { + continue; + } + + states = states.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); + } + + var action = new Raid + { + AvatarAddress = _avatarAddress, + EquipmentIds = new List(), + CostumeIds = new List(), + RuneInfos = new List(), + FoodIds = new List(), + }; + action.Execute(new ActionContext + { + PreviousState = states, + Signer = _agentAddress, + RandomSeed = 0, + BlockIndex = _tableSheets.WorldBossListSheet.First().Value.StartedBlockIndex, + }); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void BattleArena(bool collectionExist) + { + var prevStates = _initialState; + if (collectionExist) + { + var collectionState = new CollectionState(); + collectionState.Ids.Add(1); + prevStates = prevStates.SetCollectionState(_avatarAddress, collectionState); + } + + foreach (var (key, value) in _sheets) + { + if (key == nameof(CollectionSheet) && !collectionExist) + { + continue; + } + + prevStates = prevStates.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); + } + + var addresses = new[] { _avatarAddress, _enemyAvatarAddress }; + foreach (var avatarAddress in addresses) + { + var itemSlotStateAddress = ItemSlotState.DeriveAddress(avatarAddress, BattleType.Arena); + Assert.Null(_initialState.GetLegacyState(itemSlotStateAddress)); + + var avatarState = prevStates.GetAvatarState(avatarAddress); + for (int i = 0; i < 50; i++) + { + avatarState.worldInformation.ClearStage(1, i + 1, 0, _tableSheets.WorldSheet, _tableSheets.WorldUnlockSheet); + } + + prevStates = prevStates.SetAvatarState( + avatarAddress, avatarState, false, false, true, false); + + var join = new JoinArena3 + { + avatarAddress = avatarAddress, + championshipId = 1, + round = 1, + costumes = new List(), + equipments = new List(), + runeInfos = new List(), + }; + var nextState = join.Execute(new ActionContext + { + BlockIndex = 1, + Signer = _agentAddress, + PreviousState = prevStates, + }); + prevStates = nextState; + } + + foreach (var avatarAddress in addresses) + { + var enemyAvatarAddress = avatarAddress.Equals(_avatarAddress) + ? _enemyAvatarAddress + : _avatarAddress; + var battle = new BattleArena + { + myAvatarAddress = avatarAddress, + enemyAvatarAddress = enemyAvatarAddress, + championshipId = 1, + round = 1, + ticket = 1, + costumes = new List(), + equipments = new List(), + runeInfos = new List(), + }; + + battle.Execute(new ActionContext + { + Signer = _agentAddress, + PreviousState = prevStates, + BlockIndex = 2, + RandomSeed = 0, + }); + } + } + } +} diff --git a/.Lib9c.Tests/Model/ArenaSimulatorTest.cs b/.Lib9c.Tests/Model/ArenaSimulatorTest.cs index 84ed1a2627..2e2f65118d 100644 --- a/.Lib9c.Tests/Model/ArenaSimulatorTest.cs +++ b/.Lib9c.Tests/Model/ArenaSimulatorTest.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests using Nekoyume.Arena; using Nekoyume.Model; using Nekoyume.Model.BattleStatus.Arena; + using Nekoyume.Model.Stat; using Nekoyume.Model.State; using Xunit; @@ -54,7 +55,19 @@ public void Simulate() var myDigest = new ArenaPlayerDigest(_avatarState1, _arenaAvatarState1); var enemyDigest = new ArenaPlayerDigest(_avatarState2, _arenaAvatarState2); var arenaSheets = _tableSheets.GetArenaSimulatorSheets(); - var log = simulator.Simulate(myDigest, enemyDigest, arenaSheets); + var log = simulator.Simulate( + myDigest, + enemyDigest, + arenaSheets, + new List + { + new (StatType.ATK, StatModifier.OperationType.Add, 1), + }, + new List + { + new (StatType.DEF, StatModifier.OperationType.Add, 1), + } + ); Assert.Equal(_random, simulator.Random); @@ -73,8 +86,10 @@ public void Simulate() Assert.Equal(2, players.Count()); Assert.Equal(2, arenaCharacters.Count); - Assert.Equal(1, arenaCharacters.Count(x => x.IsEnemy)); - Assert.Equal(1, arenaCharacters.Count(x => !x.IsEnemy)); + var challenger = arenaCharacters.Single(a => !a.IsEnemy); + var enemy = arenaCharacters.Single(a => a.IsEnemy); + Assert.Equal(enemy.ATK + 1, challenger.ATK); + Assert.Equal(challenger.DEF + 1, enemy.DEF); var dead = log.Events.OfType(); Assert.Single(dead); @@ -101,7 +116,7 @@ public void HpIncreasingModifier(int? modifier) var myDigest = new ArenaPlayerDigest(_avatarState1, _arenaAvatarState1); var enemyDigest = new ArenaPlayerDigest(_avatarState2, _arenaAvatarState2); var arenaSheets = _tableSheets.GetArenaSimulatorSheets(); - var log = simulator.Simulate(myDigest, enemyDigest, arenaSheets); + var log = simulator.Simulate(myDigest, enemyDigest, arenaSheets, new List(), new List()); var expectedHpModifier = modifier ?? 2; Assert.Equal(_random, simulator.Random); diff --git a/.Lib9c.Tests/Model/BattleLogTest.cs b/.Lib9c.Tests/Model/BattleLogTest.cs index 96e19611ed..e5311fbff0 100644 --- a/.Lib9c.Tests/Model/BattleLogTest.cs +++ b/.Lib9c.Tests/Model/BattleLogTest.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Model using Libplanet.Crypto; using Nekoyume.Battle; using Nekoyume.Model.BattleStatus; + using Nekoyume.Model.Stat; using Nekoyume.Model.State; using Xunit; @@ -51,7 +52,8 @@ public void IsClearBeforeSimulate() StageSimulator.GetWaveRewards( _random, _tableSheets.StageSheet[1], - _tableSheets.MaterialItemSheet) + _tableSheets.MaterialItemSheet), + new List() ); Assert.False(simulator.Log.IsClear); } diff --git a/.Lib9c.Tests/Model/PlayerTest.cs b/.Lib9c.Tests/Model/PlayerTest.cs index 10df5aac06..4e35f6252c 100644 --- a/.Lib9c.Tests/Model/PlayerTest.cs +++ b/.Lib9c.Tests/Model/PlayerTest.cs @@ -59,7 +59,8 @@ public void TickAlive() StageSimulator.GetWaveRewards( _random, _tableSheets.StageSheet[1], - _tableSheets.MaterialItemSheet) + _tableSheets.MaterialItemSheet), + new List() ); var player = simulator.Player; var enemy = new Enemy(player, _tableSheets.CharacterSheet.Values.First(), 1); @@ -92,7 +93,8 @@ public void TickDead() StageSimulator.GetWaveRewards( _random, _tableSheets.StageSheet[1], - _tableSheets.MaterialItemSheet) + _tableSheets.MaterialItemSheet), + new List() ); var player = simulator.Player; var enemy = new Enemy(player, _tableSheets.CharacterSheet.Values.First(), 1); @@ -142,7 +144,8 @@ public void UseDoubleAttack(SkillCategory skillCategory) StageSimulator.GetWaveRewards( _random, _tableSheets.StageSheet[1], - _tableSheets.MaterialItemSheet) + _tableSheets.MaterialItemSheet), + new List() ); var player = simulator.Player; @@ -192,7 +195,8 @@ public void UseAuraSkill() StageSimulator.GetWaveRewards( _random, _tableSheets.StageSheet[1], - _tableSheets.MaterialItemSheet) + _tableSheets.MaterialItemSheet), + new List() ); var player = simulator.Player; var enemy = new Enemy(player, _tableSheets.CharacterSheet.Values.First(), 1); @@ -246,7 +250,8 @@ public void UseAuraBuffWithFood() StageSimulator.GetWaveRewards( _random, _tableSheets.StageSheet[1], - _tableSheets.MaterialItemSheet) + _tableSheets.MaterialItemSheet), + new List() ); var player = simulator.Player; var enemy = new Enemy(player, _tableSheets.CharacterSheet.Values.First(), 1); @@ -373,7 +378,8 @@ public void GetExpV3(int nextLevel, bool log) StageSimulator.GetWaveRewards( _random, _tableSheets.StageSheet[1], - _tableSheets.MaterialItemSheet) + _tableSheets.MaterialItemSheet), + new List() ); var player = simulator.Player; Assert.Empty(player.eventMap); @@ -451,7 +457,8 @@ public void GetStun() StageSimulator.GetWaveRewards( _random, _tableSheets.StageSheet[1], - _tableSheets.MaterialItemSheet) + _tableSheets.MaterialItemSheet), + new List() ); var player = simulator.Player; var enemy = new Enemy(player, _tableSheets.CharacterSheet.Values.First(), 1); @@ -519,7 +526,8 @@ public void GiveStun() StageSimulator.GetWaveRewards( _random, _tableSheets.StageSheet[1], - _tableSheets.MaterialItemSheet) + _tableSheets.MaterialItemSheet), + new List() ); var skill = SkillFactory.Get(_tableSheets.SkillSheet[700004], 0, 100, 0, StatType.NONE); skill.CustomField = new SkillCustomField { BuffDuration = 2 }; @@ -601,7 +609,8 @@ public void Vampiric(int duration, int percent) StageSimulator.GetWaveRewards( _random, _tableSheets.StageSheet[1], - _tableSheets.MaterialItemSheet) + _tableSheets.MaterialItemSheet), + new List() ); var player = simulator.Player; var enemy = new Enemy( diff --git a/.Lib9c.Tests/Model/RaidSimulatorV3Test.cs b/.Lib9c.Tests/Model/RaidSimulatorV3Test.cs index 843cda025e..1a0eaf205f 100644 --- a/.Lib9c.Tests/Model/RaidSimulatorV3Test.cs +++ b/.Lib9c.Tests/Model/RaidSimulatorV3Test.cs @@ -7,6 +7,7 @@ namespace Lib9c.Tests.Model using Libplanet.Action; using Nekoyume.Battle; using Nekoyume.Model.BattleStatus; + using Nekoyume.Model.Stat; using Nekoyume.Model.State; using Xunit; @@ -44,8 +45,14 @@ public void Simulate() new List(), null, _tableSheets.GetRaidSimulatorSheets(), - _tableSheets.CostumeStatSheet); + _tableSheets.CostumeStatSheet, + new List + { + new (StatType.DEF, StatModifier.OperationType.Percentage, 100), + }); Assert.Equal(_random, simulator.Random); + Assert.Equal(simulator.Player.Stats.BaseStats.DEF * 2, simulator.Player.Stats.DEF); + Assert.Equal(simulator.Player.Stats.BaseStats.DEF, simulator.Player.Stats.CollectionStats.DEF); var log = simulator.Simulate(); diff --git a/.Lib9c.Tests/Model/Skill/NormalAttackTest.cs b/.Lib9c.Tests/Model/Skill/NormalAttackTest.cs index bf73f2aa14..daeb855469 100644 --- a/.Lib9c.Tests/Model/Skill/NormalAttackTest.cs +++ b/.Lib9c.Tests/Model/Skill/NormalAttackTest.cs @@ -67,6 +67,7 @@ public void Use(bool copyCharacter) random, tableSheets.StageSheet[1], tableSheets.MaterialItemSheet), + new List(), copyCharacter ); var player = new Player(avatarState, simulator); diff --git a/.Lib9c.Tests/Model/StageSimulatorTest.cs b/.Lib9c.Tests/Model/StageSimulatorTest.cs index 8935de419f..519f0b4dc7 100644 --- a/.Lib9c.Tests/Model/StageSimulatorTest.cs +++ b/.Lib9c.Tests/Model/StageSimulatorTest.cs @@ -60,11 +60,17 @@ public void Simulate() StageSimulator.GetWaveRewards( _random, _tableSheets.StageSheet[1], - _tableSheets.MaterialItemSheet) + _tableSheets.MaterialItemSheet), + new List + { + new (StatType.ATK, StatModifier.OperationType.Add, 100), + } ); var player = simulator.Player; Assert.Equal(row.Stat, player.Stats.OptionalStats.ATK); + Assert.Equal(100, player.Stats.CollectionStats.ATK); + Assert.Equal(100 + row.Stat + player.Stats.BaseStats.ATK, player.Stats.ATK); while (player.Level == 1) { simulator.Simulate(); diff --git a/Lib9c/Action/BattleArena.cs b/Lib9c/Action/BattleArena.cs index 832f82adcd..b2dcc02d4f 100644 --- a/Lib9c/Action/BattleArena.cs +++ b/Lib9c/Action/BattleArena.cs @@ -16,6 +16,7 @@ using Nekoyume.Model.BattleStatus.Arena; using Nekoyume.Model.EnumType; using Nekoyume.Model.Item; +using Nekoyume.Model.Stat; using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.TableData; @@ -117,18 +118,26 @@ public override IWorld Execute(IActionContext context) $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); } + var collectionStates = + states.GetCollectionStates(new[]{ myAvatarAddress, enemyAvatarAddress }); + var collectionExist = collectionStates.Any(r => r is not null); + var sheetTypes = new List + { + typeof(ArenaSheet), + typeof(ItemRequirementSheet), + typeof(EquipmentItemRecipeSheet), + typeof(EquipmentItemSubRecipeSheetV2), + typeof(EquipmentItemOptionSheet), + typeof(MaterialItemSheet), + typeof(RuneListSheet), + }; + if (collectionExist) + { + sheetTypes.Add(typeof(CollectionSheet)); + } var sheets = states.GetSheets( containArenaSimulatorSheets: true, - sheetTypes: new[] - { - typeof(ArenaSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(MaterialItemSheet), - typeof(RuneListSheet), - }); + sheetTypes: sheetTypes); var gameConfigState = states.GetGameConfigState(); avatarState.ValidEquipmentAndCostumeV2(costumes, equipments, @@ -374,6 +383,29 @@ public override IWorld Execute(IActionContext context) var defeatCount = 0; var rewards = new List(); var random = context.GetRandom(); + var modifiers = new List> + { + new(), + new(), + }; + if (collectionExist) + { + var collectionSheet = sheets.GetSheet(); + for (int i = 0; i < collectionStates.Count; i++) + { + var state = collectionStates[i]; + if (state is null) + { + continue; + } + + var modifier = modifiers[i]; + foreach (var collectionId in state.Ids) + { + modifier.AddRange(collectionSheet[collectionId].StatModifiers); + } + } + } for (var i = 0; i < ticket; i++) { var simulator = new ArenaSimulator(random, HpIncreasingModifier); @@ -381,6 +413,8 @@ public override IWorld Execute(IActionContext context) myArenaPlayerDigest, enemyArenaPlayerDigest, arenaSheets, + modifiers[0], + modifiers[1], true); if (log.Result.Equals(ArenaLog.ArenaResult.Win)) { diff --git a/Lib9c/Action/EventDungeonBattle.cs b/Lib9c/Action/EventDungeonBattle.cs index a6fa4b1bc1..2fe1f51b1a 100644 --- a/Lib9c/Action/EventDungeonBattle.cs +++ b/Lib9c/Action/EventDungeonBattle.cs @@ -14,6 +14,7 @@ using Nekoyume.Model.EnumType; using Nekoyume.Model.Event; using Nekoyume.Model.Skill; +using Nekoyume.Model.Stat; using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.TableData; @@ -347,7 +348,8 @@ is Bencodex.Types.List serializedEventDungeonInfoList random, stageRow, sheets.GetSheet(), - PlayCount)); + PlayCount), + new List()); simulator.Simulate(); sw.Stop(); Log.Verbose( diff --git a/Lib9c/Action/HackAndSlash.cs b/Lib9c/Action/HackAndSlash.cs index 47ef37abca..4683d01692 100644 --- a/Lib9c/Action/HackAndSlash.cs +++ b/Lib9c/Action/HackAndSlash.cs @@ -11,6 +11,7 @@ using Nekoyume.Extensions; using Nekoyume.Model.EnumType; using Nekoyume.Model.Item; +using Nekoyume.Model.Stat; using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.TableData; @@ -112,10 +113,6 @@ public IWorld Execute( long blockIndex, IRandom random) { - var inventoryAddress = AvatarAddress.Derive(LegacyInventoryKey); - var worldInformationAddress = AvatarAddress.Derive(LegacyWorldInformationKey); - var questListAddress = AvatarAddress.Derive(LegacyQuestListKey); - var addressesHex = $"[{signer.ToHex()}, {AvatarAddress.ToHex()}]"; var started = DateTimeOffset.UtcNow; const string source = "HackAndSlash"; @@ -158,31 +155,37 @@ public IWorld Execute( addressesHex, source, "Get AvatarState", blockIndex, sw.Elapsed.TotalMilliseconds); sw.Restart(); + var collectionExist = states.TryGetCollectionState(AvatarAddress, out var collectionState) && collectionState.Ids.Any(); + var sheetTypes = new List + { + typeof(WorldSheet), + typeof(StageSheet), + typeof(StageWaveSheet), + typeof(EnemySkillSheet), + typeof(CostumeStatSheet), + typeof(SkillSheet), + typeof(QuestRewardSheet), + typeof(QuestItemRewardSheet), + typeof(EquipmentItemRecipeSheet), + typeof(WorldUnlockSheet), + typeof(MaterialItemSheet), + typeof(ItemRequirementSheet), + typeof(EquipmentItemRecipeSheet), + typeof(EquipmentItemSubRecipeSheetV2), + typeof(EquipmentItemOptionSheet), + typeof(CrystalStageBuffGachaSheet), + typeof(CrystalRandomBuffSheet), + typeof(StakeActionPointCoefficientSheet), + typeof(RuneListSheet), + }; + if (collectionExist) + { + sheetTypes.Add(typeof(CollectionSheet)); + } var sheets = states.GetSheets( containQuestSheet: true, containSimulatorSheets: true, - sheetTypes: new[] - { - typeof(WorldSheet), - typeof(StageSheet), - typeof(StageWaveSheet), - typeof(EnemySkillSheet), - typeof(CostumeStatSheet), - typeof(SkillSheet), - typeof(QuestRewardSheet), - typeof(QuestItemRewardSheet), - typeof(EquipmentItemRecipeSheet), - typeof(WorldUnlockSheet), - typeof(MaterialItemSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(CrystalStageBuffGachaSheet), - typeof(CrystalRandomBuffSheet), - typeof(StakeActionPointCoefficientSheet), - typeof(RuneListSheet), - }); + sheetTypes: sheetTypes); sw.Stop(); Log.Verbose("{AddressesHex} {Source} HAS {Process} from #{BlockIndex}: {Elapsed}", addressesHex, source, "Get Sheets", blockIndex, sw.Elapsed.TotalMilliseconds); @@ -459,6 +462,15 @@ public IWorld Execute( var costumeStatSheet = sheets.GetSheet(); var stageCleared = !isNotClearedStage; var starCount = 0; + var collectionModifiers = new List(); + if (collectionExist) + { + var collectionSheet = sheets.GetSheet(); + foreach (var collectionId in collectionState.Ids) + { + collectionModifiers.AddRange(collectionSheet[collectionId].StatModifiers); + } + } for (var i = 0; i < TotalPlayCount; i++) { var rewards = StageSimulator.GetWaveRewards(random, stageRow, materialItemSheet); @@ -481,6 +493,7 @@ public IWorld Execute( enemySkillSheet, costumeStatSheet, rewards, + collectionModifiers, false); sw.Stop(); Log.Verbose("{AddressesHex} {Source} HAS {Process} from #{BlockIndex}: {Elapsed}", diff --git a/Lib9c/Action/Raid.cs b/Lib9c/Action/Raid.cs index bb7eea941c..7cba0dfea8 100644 --- a/Lib9c/Action/Raid.cs +++ b/Lib9c/Action/Raid.cs @@ -14,6 +14,7 @@ using Nekoyume.Model.Arena; using Nekoyume.Model.EnumType; using Nekoyume.Model.Item; +using Nekoyume.Model.Stat; using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.TableData; @@ -59,9 +60,9 @@ public override IWorld Execute(IActionContext context) $"Aborted as the avatar state of the signer was failed to load."); } - Dictionary sheets = states.GetSheets( - containRaidSimulatorSheets: true, - sheetTypes: new [] { + var collectionExist = states.TryGetCollectionState(AvatarAddress, out var collectionState) && collectionState.Ids.Any(); + var sheetTypes = new List + { typeof(MaterialItemSheet), typeof(SkillSheet), typeof(SkillBuffSheet), @@ -82,7 +83,15 @@ public override IWorld Execute(IActionContext context) typeof(WorldBossKillRewardSheet), typeof(RuneSheet), typeof(RuneListSheet), - }); + }; + if (collectionExist) + { + sheetTypes.Add(typeof(CollectionSheet)); + } + + Dictionary sheets = states.GetSheets( + containRaidSimulatorSheets: true, + sheetTypes: sheetTypes); var worldBossListSheet = sheets.GetSheet(); var row = worldBossListSheet.FindRowByBlockIndex(context.BlockIndex); int raidId = row.Id; @@ -203,6 +212,15 @@ public override IWorld Execute(IActionContext context) } } + var collectionModifiers = new List(); + if (collectionExist) + { + var collectionSheet = sheets.GetSheet(); + foreach (var collectionId in collectionState.Ids) + { + collectionModifiers.AddRange(collectionSheet[collectionId].StatModifiers); + } + } // Simulate. var random = context.GetRandom(); var simulator = new RaidSimulator( @@ -212,7 +230,9 @@ public override IWorld Execute(IActionContext context) FoodIds, runeStates, raidSimulatorSheets, - sheets.GetSheet()); + sheets.GetSheet(), + collectionModifiers + ); simulator.Simulate(); avatarState.inventory = simulator.Player.Inventory; diff --git a/Lib9c/Arena/ArenaSimulator.cs b/Lib9c/Arena/ArenaSimulator.cs index 4388a1462d..30188d0be2 100644 --- a/Lib9c/Arena/ArenaSimulator.cs +++ b/Lib9c/Arena/ArenaSimulator.cs @@ -3,6 +3,7 @@ using Libplanet.Action; using Nekoyume.Model; using Nekoyume.Model.BattleStatus.Arena; +using Nekoyume.Model.Stat; using Nekoyume.TableData; using Priority_Queue; @@ -32,10 +33,12 @@ public ArenaLog Simulate( ArenaPlayerDigest challenger, ArenaPlayerDigest enemy, ArenaSimulatorSheets sheets, + List challengerCollectionModifiers, + List enemyCollectionModifiers, bool setExtraValueBuffBeforeGetBuffs = false) { Log = new ArenaLog(); - var players = SpawnPlayers(this, challenger, enemy, sheets, Log, setExtraValueBuffBeforeGetBuffs); + var players = SpawnPlayers(this, challenger, enemy, sheets, Log, challengerCollectionModifiers, enemyCollectionModifiers, setExtraValueBuffBeforeGetBuffs); Turn = 1; while (true) @@ -103,6 +106,8 @@ private static SimplePriorityQueue SpawnPlayers( ArenaPlayerDigest enemyDigest, ArenaSimulatorSheets simulatorSheets, ArenaLog log, + List challengerCollectionModifiers, + List enemyCollectionModifiers, bool setExtraValueBuffBeforeGetBuffs = false) { var challenger = new ArenaCharacter( @@ -119,6 +124,8 @@ private static SimplePriorityQueue SpawnPlayers( simulatorSheets.SkillSheet); } + challenger.Stats.SetCollections(challengerCollectionModifiers); + var enemy = new ArenaCharacter( simulator, enemyDigest, @@ -134,6 +141,8 @@ private static SimplePriorityQueue SpawnPlayers( simulatorSheets.SkillSheet); } + enemy.Stats.SetCollections(enemyCollectionModifiers); + challenger.Spawn(enemy); enemy.Spawn(challenger); diff --git a/Lib9c/Battle/RaidSimulator.cs b/Lib9c/Battle/RaidSimulator.cs index 4dbeb528bf..321e2ca95a 100644 --- a/Lib9c/Battle/RaidSimulator.cs +++ b/Lib9c/Battle/RaidSimulator.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Nekoyume.Model.Stat; namespace Nekoyume.Battle { @@ -33,7 +34,8 @@ public RaidSimulator( List foods, List runeStates, RaidSimulatorSheets simulatorSheets, - CostumeStatSheet costumeStatSheet) : base(random, avatarState, foods, simulatorSheets) + CostumeStatSheet costumeStatSheet, + List collectionModifiers) : base(random, avatarState, foods, simulatorSheets) { Player.SetCostumeStat(costumeStatSheet); if (runeStates != null) @@ -41,6 +43,8 @@ public RaidSimulator( Player.SetRune(runeStates, simulatorSheets.RuneOptionSheet, simulatorSheets.SkillSheet); } + Player.Stats.SetCollections(collectionModifiers); + BossId = bossId; _waves = new List(); diff --git a/Lib9c/Battle/StageSimulator.cs b/Lib9c/Battle/StageSimulator.cs index 216e8f0be1..7855a57a03 100644 --- a/Lib9c/Battle/StageSimulator.cs +++ b/Lib9c/Battle/StageSimulator.cs @@ -47,6 +47,7 @@ public StageSimulator(IRandom random, EnemySkillSheet enemySkillSheet, CostumeStatSheet costumeStatSheet, List waveRewards, + List collectionModifiers, bool logEvent = true) : base( random, @@ -61,6 +62,8 @@ public StageSimulator(IRandom random, Player.SetRune(runeStates, simulatorSheets.RuneOptionSheet, simulatorSheets.SkillSheet); } + Player.Stats.SetCollections(collectionModifiers); + _waves = new List(); _waveRewards = waveRewards; WorldId = worldId; From 1fcea5a1d9a2040a0e1b9005a60e0efeec35cacc Mon Sep 17 00:00:00 2001 From: jaeho0103 Date: Tue, 6 Feb 2024 14:44:09 +0900 Subject: [PATCH 17/64] collection sheet draft data --- Lib9c/TableCSV/CollectionSheet.csv | 360 ++++++++++++++++++++++++++++- 1 file changed, 357 insertions(+), 3 deletions(-) diff --git a/Lib9c/TableCSV/CollectionSheet.csv b/Lib9c/TableCSV/CollectionSheet.csv index 99657dd6eb..10641f6670 100644 --- a/Lib9c/TableCSV/CollectionSheet.csv +++ b/Lib9c/TableCSV/CollectionSheet.csv @@ -1,3 +1,357 @@ -id,item_id,count,level,option_count,skill,item_id,count,level,option_count,skill,item_id,count,level,option_count,skill,item_id,count,level,option_count,skill,item_id,count,level,option_count,skill,item_id,count,level,option_count,skill,stat_type,modify_type,modify_value,stat_type,modify_type,modify_value,stat_type,modify_type,modify_value -1,10110000,1,0,,,302000,2,,,,,,,,,,,,,,,,,,,,,,,,ATK,Add,1,,,,,, -2,10110000,1,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,1,,,,,, +id,item_id1,count1,level1,skill1,item_id2,count2,level2,skill2,item_id3,count3,level3,skill3,item_id4,count4,level4,skill4,item_id5,count5,level5,skill5,item_id6,count6,level6,skill6,stat_type1,modify_type1,modify_value1,stat_type2,modify_type2,modify_value2,stat_type3,modify_type3,modify_value3 +1,10114000,1,0,TRUE,10120000,1,0,TRUE,10124000,1,0,TRUE,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +2,10133000,1,0,TRUE,10134000,1,0,TRUE,10132001,1,0,TRUE,10133001,1,0,TRUE,10134001,1,0,TRUE,,,,,ATK,Add,100,,,,,, +3,10140000,1,0,TRUE,10141000,1,0,TRUE,10142000,1,0,TRUE,10143000,1,0,TRUE,10144000,1,0,TRUE,,,,,ATK,Add,100,,,,,, +4,10150000,1,0,TRUE,10151000,1,0,TRUE,10152000,1,0,TRUE,10153000,1,0,TRUE,10154000,1,0,TRUE,,,,,ATK,Add,100,,,,,, +5,10211000,1,0,TRUE,10214000,1,0,TRUE,10222000,1,0,TRUE,10224000,1,0,TRUE,,,,,,,,,HP,Add,10000,,,,,, +6,10233000,1,0,TRUE,10234000,1,0,TRUE,,,,,,,,,,,,,,,,,HP,Add,10000,,,,,, +7,10230001,1,0,TRUE,10231001,1,0,TRUE,10232001,1,0,TRUE,10233001,1,0,TRUE,,,,,,,,,HP,Add,10000,,,,,, +8,10240000,1,0,TRUE,10241000,1,0,TRUE,10242000,1,0,TRUE,10243000,1,0,TRUE,10244000,1,0,TRUE,,,,,HP,Add,10000,,,,,, +9,10250001,1,0,TRUE,10251001,1,0,TRUE,10252001,1,0,TRUE,10253001,1,0,TRUE,10254001,1,0,TRUE,,,,,HP,Add,100,,,,,, +10,10314000,1,0,TRUE,10323000,1,0,TRUE,10324000,1,0,TRUE,,,,,,,,,,,,,HIT,Add,100,,,,,, +11,10331000,1,0,TRUE,10334000,1,0,TRUE,10343000,1,0,TRUE,,,,,,,,,,,,,HIT,Add,100,,,,,, +12,10350000,1,0,TRUE,10351000,1,0,TRUE,10352000,1,0,TRUE,10353000,1,0,TRUE,10354000,1,0,TRUE,,,,,HIT,Add,100,,,,,, +13,10413000,1,0,TRUE,10423000,1,0,TRUE,10424000,1,0,TRUE,,,,,,,,,,,,,SPD,Add,100,,,,,, +14,10433000,1,0,TRUE,10434000,1,0,TRUE,10440000,1,0,TRUE,,,,,,,,,,,,,SPD,Add,100,,,,,, +15,10450000,1,0,TRUE,10451000,1,0,TRUE,10452000,1,0,TRUE,10453000,1,0,TRUE,10454000,1,0,TRUE,,,,,SPD,Add,100,,,,,, +16,10510000,1,0,TRUE,10514000,1,0,TRUE,,,,,,,,,,,,,,,,,DEF,Add,100,,,,,, +17,10521000,1,0,TRUE,10523000,1,0,TRUE,10524000,1,0,TRUE,,,,,,,,,,,,,DEF,Add,100,,,,,, +18,10531000,1,0,TRUE,10533000,1,0,TRUE,10534000,1,0,TRUE,,,,,,,,,,,,,DEF,Add,100,,,,,, +19,10540000,1,0,TRUE,10541000,1,0,TRUE,10542000,1,0,TRUE,10543000,1,0,TRUE,10544000,1,0,TRUE,,,,,DEF,Add,100,,,,,, +20,10550000,1,0,TRUE,10551000,1,0,TRUE,10552000,1,0,TRUE,10553000,1,0,TRUE,10554000,1,0,TRUE,,,,,DEF,Add,100,,,,,, +21,10350001,1,0,TRUE,10351001,1,0,TRUE,10352001,1,0,TRUE,10353001,1,0,TRUE,10354001,1,0,TRUE,,,,,HIT,Add,100,,,,,, +22,10450001,1,0,TRUE,10451001,1,0,TRUE,10452001,1,0,TRUE,10453001,1,0,TRUE,10454001,1,0,TRUE,,,,,SPD,Add,100,,,,,, +23,10114000,1,2,TRUE,10120000,1,2,TRUE,10124000,1,2,TRUE,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +24,10133000,1,2,TRUE,10134000,1,2,TRUE,10132001,1,2,TRUE,10133001,1,2,TRUE,10134001,1,2,TRUE,,,,,ATK,Add,100,,,,,, +25,10140000,1,2,TRUE,10141000,1,2,TRUE,10142000,1,2,TRUE,10143000,1,2,TRUE,10144000,1,2,TRUE,,,,,ATK,Add,100,,,,,, +26,10150000,1,2,TRUE,10151000,1,2,TRUE,10152000,1,2,TRUE,10153000,1,2,TRUE,10154000,1,2,TRUE,,,,,ATK,Add,100,,,,,, +27,10211000,1,2,TRUE,10214000,1,2,TRUE,10222000,1,2,TRUE,10224000,1,2,TRUE,,,,,,,,,HP,Add,10000,,,,,, +28,10233000,1,2,TRUE,10234000,1,2,TRUE,,,,,,,,,,,,,,,,,HP,Add,10000,,,,,, +29,10230001,1,2,TRUE,10231001,1,2,TRUE,10232001,1,2,TRUE,10233001,1,2,TRUE,,,,,,,,,HP,Add,10000,,,,,, +30,10240000,1,2,TRUE,10241000,1,2,TRUE,10242000,1,2,TRUE,10243000,1,2,TRUE,10244000,1,2,TRUE,,,,,HP,Add,10000,,,,,, +31,10250001,1,2,TRUE,10251001,1,2,TRUE,10252001,1,2,TRUE,10253001,1,2,TRUE,10254001,1,2,TRUE,,,,,HP,Add,100,,,,,, +32,10314000,1,2,TRUE,10323000,1,2,TRUE,10324000,1,2,TRUE,,,,,,,,,,,,,HIT,Add,100,,,,,, +33,10331000,1,2,TRUE,10334000,1,2,TRUE,10343000,1,2,TRUE,,,,,,,,,,,,,HIT,Add,100,,,,,, +34,10350000,1,2,TRUE,10351000,1,2,TRUE,10352000,1,2,TRUE,10353000,1,2,TRUE,10354000,1,2,TRUE,,,,,HIT,Add,100,,,,,, +35,10413000,1,2,TRUE,10423000,1,2,TRUE,10424000,1,2,TRUE,,,,,,,,,,,,,SPD,Add,100,,,,,, +36,10433000,1,2,TRUE,10434000,1,2,TRUE,10440000,1,2,TRUE,,,,,,,,,,,,,SPD,Add,100,,,,,, +37,10450000,1,2,TRUE,10451000,1,2,TRUE,10452000,1,2,TRUE,10453000,1,2,TRUE,10454000,1,2,TRUE,,,,,SPD,Add,100,,,,,, +38,10510000,1,2,TRUE,10514000,1,2,TRUE,,,,,,,,,,,,,,,,,DEF,Add,100,,,,,, +39,10521000,1,2,TRUE,10523000,1,2,TRUE,10524000,1,2,TRUE,,,,,,,,,,,,,DEF,Add,100,,,,,, +40,10531000,1,2,TRUE,10533000,1,2,TRUE,10534000,1,2,TRUE,,,,,,,,,,,,,DEF,Add,100,,,,,, +41,10540000,1,2,TRUE,10541000,1,2,TRUE,10542000,1,2,TRUE,10543000,1,2,TRUE,10544000,1,2,TRUE,,,,,DEF,Add,100,,,,,, +42,10550000,1,2,TRUE,10551000,1,2,TRUE,10552000,1,2,TRUE,10553000,1,2,TRUE,10554000,1,2,TRUE,,,,,DEF,Add,100,,,,,, +43,10350001,1,2,TRUE,10351001,1,2,TRUE,10352001,1,2,TRUE,10353001,1,2,TRUE,10354001,1,2,TRUE,,,,,HIT,Add,100,,,,,, +44,10450001,1,2,TRUE,10451001,1,2,TRUE,10452001,1,2,TRUE,10453001,1,2,TRUE,10454001,1,2,TRUE,,,,,SPD,Add,100,,,,,, +45,10114000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +46,10120000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +47,10124000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +48,10133000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +49,10134000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +50,10132001,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +51,10133001,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +52,10134001,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +53,10140000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +54,10141000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +55,10142000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +56,10143000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +57,10144000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +58,10150000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +59,10151000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +60,10152000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +61,10153000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +62,10154000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +63,10211000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +64,10214000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +65,10222000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +66,10224000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +67,10233000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +68,10234000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +69,10230001,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +70,10231001,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +71,10232001,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +72,10233001,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +73,10240000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +74,10241000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +75,10242000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +76,10243000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +77,10244000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +78,10250001,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +79,10251001,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +80,10252001,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +81,10253001,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +82,10254001,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +83,10314000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +84,10323000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +85,10324000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +86,10331000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +87,10334000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +88,10343000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +89,10350000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +90,10351000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +91,10352000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +92,10353000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +93,10354000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +94,10413000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +95,10423000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +96,10424000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +97,10433000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +98,10434000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +99,10440000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +100,10450000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +101,10451000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +102,10452000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +103,10453000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +104,10454000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +105,10510000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +106,10514000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +107,10521000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +108,10523000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +109,10524000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +110,10531000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +111,10533000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +112,10534000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +113,10540000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +114,10541000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +115,10542000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +116,10543000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +117,10544000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +118,10550000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +119,10551000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +120,10552000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +121,10553000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +122,10554000,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +123,10350001,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +124,10351001,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +125,10352001,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +126,10353001,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +127,10354001,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +128,10450001,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +129,10451001,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +130,10452001,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +131,10453001,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +132,10454001,1,4,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +133,10114000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +134,10120000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +135,10124000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +136,10133000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +137,10134000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +138,10132001,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +139,10133001,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +140,10134001,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +141,10140000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +142,10141000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +143,10142000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +144,10143000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +145,10144000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +146,10150000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +147,10151000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +148,10152000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +149,10153000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +150,10154000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,50,,,,,, +151,10211000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +152,10214000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +153,10222000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +154,10224000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +155,10233000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +156,10234000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +157,10230001,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +158,10231001,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +159,10232001,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +160,10233001,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +161,10240000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +162,10241000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +163,10242000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +164,10243000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +165,10244000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +166,10250001,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +167,10251001,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +168,10252001,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +169,10253001,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +170,10254001,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,50,,,,,, +171,10314000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +172,10323000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +173,10324000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +174,10331000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +175,10334000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +176,10343000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +177,10350000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +178,10351000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +179,10352000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +180,10353000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +181,10354000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +182,10413000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +183,10423000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +184,10424000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +185,10433000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +186,10434000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +187,10440000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +188,10450000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +189,10451000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +190,10452000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +191,10453000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +192,10454000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +193,10510000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +194,10514000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +195,10521000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +196,10523000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +197,10524000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +198,10531000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +199,10533000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +200,10534000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +201,10540000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +202,10541000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +203,10542000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +204,10543000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +205,10544000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +206,10550000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +207,10551000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +208,10552000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +209,10553000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +210,10554000,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,DEF,Percentage,50,,,,,, +211,10350001,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +212,10351001,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +213,10352001,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +214,10353001,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +215,10354001,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,HIT,Percentage,50,,,,,, +216,10450001,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +217,10451001,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +218,10452001,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +219,10453001,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +220,10454001,1,8,TRUE,,,,,,,,,,,,,,,,,,,,,SPD,Percentage,50,,,,,, +10001,201000,1,,FALSE,201001,1,,FALSE,201002,1,,FALSE,,,,,,,,,,,,,ATK,Add,100,,,,,, +10002,201003,1,,FALSE,201004,1,,FALSE,201005,1,,FALSE,,,,,,,,,,,,,ATK,Add,100,,,,,, +10003,201006,1,,FALSE,201007,1,,FALSE,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10004,201008,1,,FALSE,201009,1,,FALSE,201010,1,,FALSE,201011,1,,FALSE,,,,,,,,,ATK,Add,100,,,,,, +10005,201008,1,,FALSE,201008,1,,FALSE,201008,1,,FALSE,,,,,,,,,,,,,ATK,Add,100,,,,,, +10006,201008,1,,FALSE,201008,1,,FALSE,201008,1,,FALSE,,,,,,,,,,,,,ATK,Add,100,,,,,, +10007,201008,1,,FALSE,201008,1,,FALSE,201008,1,,FALSE,,,,,,,,,,,,,ATK,Add,100,,,,,, +10008,201008,1,,FALSE,201008,1,,FALSE,201008,1,,FALSE,,,,,,,,,,,,,ATK,Add,100,,,,,, +10009,201008,1,,FALSE,201008,1,,FALSE,201008,1,,FALSE,,,,,,,,,,,,,ATK,Add,100,,,,,, +10010,201008,1,,FALSE,201008,1,,FALSE,201008,1,,FALSE,,,,,,,,,,,,,ATK,Add,100,,,,,, +10011,201008,1,,FALSE,201008,1,,FALSE,201008,1,,FALSE,201008,1,,FALSE,,,,,,,,,ATK,Add,100,,,,,, +10012,201000,10,,FALSE,201001,10,,FALSE,201002,10,,FALSE,,,,,,,,,,,,,ATK,Add,100,,,,,, +10013,201003,10,,FALSE,201004,10,,FALSE,201005,10,,FALSE,,,,,,,,,,,,,ATK,Add,100,,,,,, +10014,201006,10,,FALSE,201007,10,,FALSE,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10015,201008,10,,FALSE,201009,10,,FALSE,201010,10,,FALSE,201011,10,,FALSE,,,,,,,,,ATK,Add,100,,,,,, +10016,201008,10,,FALSE,201008,10,,FALSE,201008,10,,FALSE,,,,,,,,,,,,,ATK,Add,100,,,,,, +10017,201008,10,,FALSE,201008,10,,FALSE,201008,10,,FALSE,,,,,,,,,,,,,ATK,Add,100,,,,,, +10018,201008,10,,FALSE,201008,10,,FALSE,201008,10,,FALSE,,,,,,,,,,,,,ATK,Add,100,,,,,, +10019,201008,10,,FALSE,201008,10,,FALSE,201008,10,,FALSE,,,,,,,,,,,,,ATK,Add,100,,,,,, +10020,201008,10,,FALSE,201008,10,,FALSE,201008,10,,FALSE,,,,,,,,,,,,,ATK,Add,100,,,,,, +10021,201008,10,,FALSE,201008,10,,FALSE,201008,10,,FALSE,,,,,,,,,,,,,ATK,Add,100,,,,,, +10022,201008,10,,FALSE,201008,10,,FALSE,201008,10,,FALSE,201008,10,,FALSE,,,,,,,,,ATK,Add,100,,,,,, +10023,201000,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10024,201001,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10025,201002,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10026,201003,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10027,201004,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10028,201005,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10029,201006,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10030,201007,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10031,201011,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10032,201012,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10033,201013,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10034,201008,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10035,201009,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10036,201010,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10037,201014,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10038,201015,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10039,201016,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10040,201017,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10041,201018,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10042,201019,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10043,201020,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10044,201021,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10045,201022,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10046,201023,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10047,201024,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10048,201025,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10049,201026,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10050,201027,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10051,201028,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10052,201029,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10053,201030,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10054,201031,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10055,201032,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10056,201033,100,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10057,201000,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10058,201001,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10059,201002,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10060,201003,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10061,201004,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10062,201005,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10063,201006,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10064,201007,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10065,201011,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10066,201012,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10067,201013,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10068,201008,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10069,201009,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10070,201010,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10071,201014,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10072,201015,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10073,201016,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10074,201017,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10075,201018,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10076,201019,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10077,201020,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10078,201021,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10079,201022,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10080,201023,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10081,201024,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10082,201025,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10083,201026,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10084,201027,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10085,201028,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10086,201029,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10087,201030,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10088,201031,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10089,201032,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10090,201033,300,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10091,201000,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10092,201001,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10093,201002,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10094,201003,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10095,201004,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10096,201005,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10097,201006,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10098,201007,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10099,201011,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10100,201012,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10101,201013,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10102,201008,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10103,201009,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10104,201010,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10105,201014,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10106,201015,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10107,201016,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10108,201017,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10109,201018,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10110,201019,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10111,201020,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10112,201021,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10113,201022,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10114,201023,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10115,201024,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10116,201025,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10117,201026,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10118,201027,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10119,201028,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10120,201029,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10121,201030,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10122,201031,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10123,201032,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +10124,201033,500,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Add,100,,,,,, +100001,40100001,1,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,20,,,,,, +100002,40100002,1,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,20,,,,,, +100003,40100003,1,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,20,,,,,, +100004,40100004,1,,FALSE,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,20,,,,,, +200001,49900001,1,,FALSE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,20,,,,,, +200002,49900002,1,,FALSE,,,,,,,,,,,,,,,,,,,,,HP,Percentage,20,,,,,, +1000001,400000,100,,FALSE,,,,,,,,,,,,,,,,,,,,,DEF,Add,100,,,,,, +1000002,500000,10,,FALSE,,,,,,,,,,,,,,,,,,,,,HIT,Add,100,,,,,, +1000006,400000,10000,,FALSE,,,,,,,,,,,,,,,,,,,,,DEF,Add,100,,,,,, +1000007,500000,100,,FALSE,,,,,,,,,,,,,,,,,,,,,HIT,Add,100,,,,,, +1000011,400000,100000,,FALSE,,,,,,,,,,,,,,,,,,,,,DEF,Add,100,,,,,, +1000012,500000,1000,,FALSE,,,,,,,,,,,,,,,,,,,,,HIT,Add,100,,,,,, \ No newline at end of file From ffc335ced9dc53fc4ba409e1aab52291a8613a34 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Tue, 6 Feb 2024 11:16:59 +0900 Subject: [PATCH 18/64] Remove OptionCount --- .Lib9c.Tests/TableData/CollectionSheetTest.cs | 34 +++++++++++++++--- Lib9c/TableCSV/CollectionSheet.csv | 2 +- Lib9c/TableData/CollectionSheet.cs | 36 +++++++++++-------- 3 files changed, 52 insertions(+), 20 deletions(-) diff --git a/.Lib9c.Tests/TableData/CollectionSheetTest.cs b/.Lib9c.Tests/TableData/CollectionSheetTest.cs index 8641ed9a38..707e4bf25b 100644 --- a/.Lib9c.Tests/TableData/CollectionSheetTest.cs +++ b/.Lib9c.Tests/TableData/CollectionSheetTest.cs @@ -1,5 +1,10 @@ namespace Lib9c.Tests.TableData { + using System.Collections.Generic; + using System.Linq; + using Lib9c.Tests.Action; + using Nekoyume.Model.Collection; + using Nekoyume.Model.Item; using Nekoyume.Model.Stat; using Nekoyume.TableData; using Xunit; @@ -9,9 +14,9 @@ public class CollectionSheetTest [Fact] public void Set() { - const string csv = @"id,item_id,count,level,option_count,skill,item_id,count,level,option_count,skillitem_id,count,level,option_count,skillitem_id,count,level,option_count,skillitem_id,count,level,option_count,skill,item_id,count,level,option_count,skill,stat_type,modify_type,modify_value,stat_type,modify_type,modify_value,stat_type,modify_type,modify_value\n -1,1,2,3,4,,,,,,,,,,,,,,,,,,,,,,,,,,,ATK,Add,1,,,,,,\n -2,2,3,4,5,,,,,,,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,1,,,,,,\n"; + const string csv = @"id,item_id,count,level,skill,item_id,count,level,skill,item_id,count,level,skill,item_id,count,level,skill,item_id,count,level,skill,item_id,count,level,option_count,skill,stat_type,modify_type,modify_value,stat_type,modify_type,modify_value,stat_type,modify_type,modify_value\n +1,1,2,3,,,,,,,,,,,,,,,,,,,,,,ATK,Add,1,,,,,,\n +2,2,3,4,,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,1,,,,,,\n"; var sheet = new CollectionSheet(); sheet.Set(csv); @@ -28,7 +33,6 @@ public void Set() Assert.Equal(id, material.ItemId); Assert.Equal(id + j + 1, material.Count); Assert.Equal(id + j + 2, material.Level); - Assert.Equal(id + j + 3, material.OptionCount); Assert.False(material.SkillContains); var modifier = row.StatModifiers[j]; @@ -38,5 +42,27 @@ public void Set() } } } + + [Theory] + [InlineData(ItemType.Equipment)] + [InlineData(ItemType.Costume)] + public void Validate(ItemType itemType) + { + var row = new TableSheets(TableSheetsImporter.ImportSheets()).ItemSheet.Values.First(r => r.ItemType == itemType); + var item = ItemFactory.CreateItem(row, new TestRandom()); + var materialInfo = new CollectionSheet.CollectionMaterial + { + ItemId = row.Id, + Count = 1, + Level = 0, + SkillContains = false, + }; + Assert.True(materialInfo.Validate((INonFungibleItem)item)); + if (item is Equipment equipment) + { + materialInfo.SkillContains = true; + Assert.False(materialInfo.Validate(equipment)); + } + } } } diff --git a/Lib9c/TableCSV/CollectionSheet.csv b/Lib9c/TableCSV/CollectionSheet.csv index 10641f6670..7fa9e3cd66 100644 --- a/Lib9c/TableCSV/CollectionSheet.csv +++ b/Lib9c/TableCSV/CollectionSheet.csv @@ -354,4 +354,4 @@ id,item_id1,count1,level1,skill1,item_id2,count2,level2,skill2,item_id3,count3,l 1000006,400000,10000,,FALSE,,,,,,,,,,,,,,,,,,,,,DEF,Add,100,,,,,, 1000007,500000,100,,FALSE,,,,,,,,,,,,,,,,,,,,,HIT,Add,100,,,,,, 1000011,400000,100000,,FALSE,,,,,,,,,,,,,,,,,,,,,DEF,Add,100,,,,,, -1000012,500000,1000,,FALSE,,,,,,,,,,,,,,,,,,,,,HIT,Add,100,,,,,, \ No newline at end of file +1000012,500000,1000,,FALSE,,,,,,,,,,,,,,,,,,,,,HIT,Add,100,,,,,, diff --git a/Lib9c/TableData/CollectionSheet.cs b/Lib9c/TableData/CollectionSheet.cs index b0f517f1b4..57532d0a6d 100644 --- a/Lib9c/TableData/CollectionSheet.cs +++ b/Lib9c/TableData/CollectionSheet.cs @@ -15,21 +15,28 @@ public class CollectionMaterial public int ItemId; public int Count; public int Level; - public int OptionCount; public bool SkillContains; - public bool Validate(ItemUsable itemUsable) + private bool Validate(Equipment equipment) { - switch (itemUsable) + return equipment.Id == ItemId && equipment.level == Level && + (equipment.Skills.Any() == SkillContains || equipment.BuffSkills.Any() == SkillContains); + } + + private bool Validate(Costume costume) + { + return costume.Id == ItemId; + } + public bool Validate(INonFungibleItem nonFungibleItem) + { + switch (nonFungibleItem) { + case Costume costume: + return Validate(costume); case Equipment equipment: - return equipment.Id == ItemId && equipment.level == Level && - equipment.GetOptionCount() == OptionCount && - (equipment.Skills.Any() == SkillContains || equipment.BuffSkills.Any() == SkillContains); - case Consumable consumable: - return consumable.Id == ItemId; + return Validate(equipment); default: - return false; + throw new ArgumentOutOfRangeException(nameof(nonFungibleItem)); } } } @@ -48,7 +55,7 @@ public override void Set(IReadOnlyList fields) Id = ParseInt(fields[0]); for (int i = 0; i < 6; i++) { - var offset = i * 5; + var offset = i * 4; if (!TryParseInt(fields[1 + offset], out var itemId) || itemId == 0) { continue; @@ -58,23 +65,22 @@ public override void Set(IReadOnlyList fields) ItemId = itemId, Count = ParseInt(fields[2 + offset]), Level = ParseInt(fields[3 + offset], 0), - OptionCount = ParseInt(fields[4 + offset], 0), - SkillContains = ParseBool(fields[5 + offset], false) + SkillContains = ParseBool(fields[4 + offset], false) }); } for (int i = 0; i < 3; i++) { var offset = i * 3; - var statType = fields[28 + offset]; + var statType = fields[25 + offset]; if (string.IsNullOrEmpty(statType)) { continue; } StatModifiers.Add(new StatModifier( (StatType) Enum.Parse(typeof(StatType), statType), - (StatModifier.OperationType) Enum.Parse(typeof(StatModifier.OperationType), fields[29 + offset]), - ParseInt(fields[30 + offset]))); + (StatModifier.OperationType) Enum.Parse(typeof(StatModifier.OperationType), fields[26 + offset]), + ParseInt(fields[27 + offset]))); } } } From 035d9d53c4e60b31eb4c351f6510c3c057e84f35 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Tue, 6 Feb 2024 11:23:23 +0900 Subject: [PATCH 19/64] ICollectionMaterial inherit IBencodable --- .../Collection/FungibleCollectionMaterial.cs | 20 ++++++++++------ Lib9c/Model/Collection/ICollectionMaterial.cs | 5 ++-- .../NonFungibleCollectionMaterial.cs | 24 ++++++++----------- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/Lib9c/Model/Collection/FungibleCollectionMaterial.cs b/Lib9c/Model/Collection/FungibleCollectionMaterial.cs index 3109edf8bd..d9335efffe 100644 --- a/Lib9c/Model/Collection/FungibleCollectionMaterial.cs +++ b/Lib9c/Model/Collection/FungibleCollectionMaterial.cs @@ -1,20 +1,22 @@ using Bencodex.Types; +using Nekoyume.Action; +using Nekoyume.Model.Item; +using Nekoyume.TableData; namespace Nekoyume.Model.Collection { public class FungibleCollectionMaterial : ICollectionMaterial { public MaterialType Type => MaterialType.Fungible; + public int ItemId { get; set; } + public int ItemCount { get; set; } - public IValue Serialize() - { - return List.Empty - .Add((int)Type) - .Add(ItemId) - .Add(ItemCount); - } + public IValue Bencoded => List.Empty + .Add((int) Type) + .Add(ItemId) + .Add(ItemCount); public FungibleCollectionMaterial(List serialized) { @@ -22,6 +24,10 @@ public FungibleCollectionMaterial(List serialized) ItemCount = (Integer)serialized[2]; } + public FungibleCollectionMaterial(IValue bencoded) : this((List) bencoded) + { + } + public FungibleCollectionMaterial() { } diff --git a/Lib9c/Model/Collection/ICollectionMaterial.cs b/Lib9c/Model/Collection/ICollectionMaterial.cs index c4612bbec2..b539c4bcf5 100644 --- a/Lib9c/Model/Collection/ICollectionMaterial.cs +++ b/Lib9c/Model/Collection/ICollectionMaterial.cs @@ -1,13 +1,12 @@ +using Bencodex; using Bencodex.Types; namespace Nekoyume.Model.Collection { - public interface ICollectionMaterial + public interface ICollectionMaterial: IBencodable { public MaterialType Type { get; } public int ItemId { get; set; } public int ItemCount { get; set; } - - public IValue Serialize(); } } diff --git a/Lib9c/Model/Collection/NonFungibleCollectionMaterial.cs b/Lib9c/Model/Collection/NonFungibleCollectionMaterial.cs index 709415b875..a450f00fb2 100644 --- a/Lib9c/Model/Collection/NonFungibleCollectionMaterial.cs +++ b/Lib9c/Model/Collection/NonFungibleCollectionMaterial.cs @@ -1,7 +1,9 @@ using System; using Bencodex.Types; +using Nekoyume.Action; using Nekoyume.Model.Item; using Nekoyume.Model.State; +using Nekoyume.TableData; namespace Nekoyume.Model.Collection { @@ -12,20 +14,15 @@ public class NonFungibleCollectionMaterial : ICollectionMaterial public int ItemCount { get; set; } public Guid NonFungibleId { get; set; } public int Level { get; set; } - public int OptionCount { get; set; } public bool SkillContains { get; set; } - public IValue Serialize() - { - return List.Empty - .Add((int)Type) - .Add(ItemId) - .Add(ItemCount) - .Add(NonFungibleId.Serialize()) - .Add(Level) - .Add(OptionCount) - .Add(SkillContains.Serialize()); - } + public IValue Bencoded => List.Empty + .Add((int)Type) + .Add(ItemId) + .Add(ItemCount) + .Add(NonFungibleId.Serialize()) + .Add(Level) + .Add(SkillContains.Serialize()); public NonFungibleCollectionMaterial(List serialized) { @@ -33,8 +30,7 @@ public NonFungibleCollectionMaterial(List serialized) ItemCount = (Integer)serialized[2]; NonFungibleId = serialized[3].ToGuid(); Level = (Integer)serialized[4]; - OptionCount = (Integer)serialized[5]; - SkillContains = serialized[6].ToBoolean(); + SkillContains = serialized[5].ToBoolean(); } public NonFungibleCollectionMaterial() From 5bccb33430fabc8cecdab11bdb2b85f0ab2673a1 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Tue, 6 Feb 2024 11:21:39 +0900 Subject: [PATCH 20/64] Refactor RemoveMaterial allow tradable material --- .Lib9c.Tests/Model/Item/InventoryTest.cs | 49 +++++++++++++++++++++++ Lib9c/Model/Item/Inventory.cs | 50 ++++++++++++++++++------ 2 files changed, 86 insertions(+), 13 deletions(-) diff --git a/.Lib9c.Tests/Model/Item/InventoryTest.cs b/.Lib9c.Tests/Model/Item/InventoryTest.cs index 2020854665..6120185129 100644 --- a/.Lib9c.Tests/Model/Item/InventoryTest.cs +++ b/.Lib9c.Tests/Model/Item/InventoryTest.cs @@ -885,6 +885,55 @@ public void SellItem(ItemType itemType, int itemCount, int sellCount, int invent Assert.Equal(inventoryCount, inventory.Items.Count); } + [Theory] + [InlineData(false, false, 0L, 0L, true)] + [InlineData(false, false, 0L, 1L, true)] + [InlineData(true, false, 0L, 0L, false)] + [InlineData(true, true, 0L, 1L, false)] + public void IsMaterialRemovable(bool locked, bool tradable, long requiredBlockIndex, long blockIndex, bool expected) + { + var row = TableSheets.MaterialItemSheet.Values.First(); + Inventory.Item item; + if (tradable) + { + var tradableMaterial = ItemFactory.CreateTradableMaterial(row); + tradableMaterial.RequiredBlockIndex = requiredBlockIndex; + item = new Inventory.Item(tradableMaterial); + } + else + { + var material = ItemFactory.CreateMaterial(row); + item = new Inventory.Item(material); + } + + if (locked) + { + item.LockUp(new OrderLock(Guid.NewGuid())); + } + + Assert.Equal(expected, Inventory.IsMaterialRemovable(item, row.Id, blockIndex)); + } + + [Fact] + public void RemoveMaterial() + { + var row = TableSheets.MaterialItemSheet.Values.First(); + var inventory = new Inventory(); + var material = ItemFactory.CreateMaterial(row); + var tradableMaterial = ItemFactory.CreateTradableMaterial(row); + tradableMaterial.RequiredBlockIndex = 1L; + inventory.AddItem(tradableMaterial); + inventory.AddItem(material); + Assert.Equal(2, inventory.Items.Count); + + // Check Non-tradable material remove first + Assert.True(inventory.RemoveMaterial(row.Id, 0L)); + var item = Assert.Single(inventory.Items); + Assert.IsType(item.item); + + Assert.True(inventory.RemoveMaterial(row.Id, 1L)); + Assert.Empty(inventory.Items); + } private static Consumable GetFirstConsumable() { var row = TableSheets.ConsumableItemSheet.First; diff --git a/Lib9c/Model/Item/Inventory.cs b/Lib9c/Model/Item/Inventory.cs index 4d5e0387bf..75920f1e5c 100644 --- a/Lib9c/Model/Item/Inventory.cs +++ b/Lib9c/Model/Item/Inventory.cs @@ -416,28 +416,53 @@ e.item is ITradableFungibleItem tradableFungibleItem && } - public bool RemoveMaterial(int id, int count = 1) + /// + /// Checks if the given item is material and can be removed from the inventory. + /// + /// The item to check for removal. + /// The ID of the material to remove. + /// The block index. + /// Returns a boolean indicating whether the item is removable. + public static bool IsMaterialRemovable(Item item, int id, long blockIndex) { - if (count <= 0) + if (item.Locked) { return false; } - List targetItems = new List(); - foreach (var item in _items) + if (item.item is Material material && material.Id == id) { - if (item.Locked) - { - continue; - } - if (item.item is Material material && material.Id == id) + if (material is TradableMaterial tradableMaterial && + tradableMaterial.RequiredBlockIndex > blockIndex) { - targetItems.Add(item); + return false; } + + return true; + } + + return false; + } + + /// + /// Remove a material from the inventory. + /// + /// The ID of the material item to remove. + /// The block index. + /// The number of materials to remove. Default value is 1. + /// True if the material is successfully removed, false otherwise. + public bool RemoveMaterial(int id, long blockIndex, int count = 1) + { + if (count <= 0) + { + return false; } + List targetItems = _items.Where(item => IsMaterialRemovable(item, id, blockIndex)).ToList(); + targetItems = targetItems - .OrderBy(e => e.count) + .OrderBy(e => e.item is ITradableItem) + .ThenBy(e => e.count) .ToList(); if (!targetItems.Any()) @@ -451,9 +476,8 @@ public bool RemoveMaterial(int id, int count = 1) return false; } - for (var i = 0; i < targetItems.Count; i++) + foreach (var item in targetItems) { - var item = targetItems[i]; if (item.count > count) { item.count -= count; From e489af0adef9ae2336e54f97dc37050ff2d30eaa Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Tue, 6 Feb 2024 11:22:03 +0900 Subject: [PATCH 21/64] Introduce RemoveConsumable --- .Lib9c.Tests/Model/Item/InventoryTest.cs | 44 ++++++++++++++++++++++++ Lib9c/Model/Item/Inventory.cs | 33 ++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/.Lib9c.Tests/Model/Item/InventoryTest.cs b/.Lib9c.Tests/Model/Item/InventoryTest.cs index 6120185129..e4ce2d2a8b 100644 --- a/.Lib9c.Tests/Model/Item/InventoryTest.cs +++ b/.Lib9c.Tests/Model/Item/InventoryTest.cs @@ -934,6 +934,50 @@ public void RemoveMaterial() Assert.True(inventory.RemoveMaterial(row.Id, 1L)); Assert.Empty(inventory.Items); } + + [Theory] + [InlineData(0L, 0L, 1)] + [InlineData(1L, 0L, 0)] + [InlineData(1L, 2L, 1)] + public void FilterConsumables(long requiredBlockIndex, long blockIndex, int expected) + { + var row = TableSheets.ConsumableItemSheet.Values.First(); + var inventory = new Inventory(); + var consumable = ItemFactory.CreateItemUsable(row, Guid.NewGuid(), requiredBlockIndex); + inventory.AddItem(consumable); + + Assert.Equal(expected, inventory.FilterConsumables(row.Id, blockIndex).Count); + } + + [Fact] + public void RemoveConsumable() + { + var row = TableSheets.ConsumableItemSheet.Values.First(); + var inventory = new Inventory(); + for (int i = 0; i < 3; i++) + { + var consumable = ItemFactory.CreateItemUsable(row, Guid.NewGuid(), 4 + i); + inventory.AddItem(consumable); + } + + Assert.Equal(3, inventory.Consumables.Count()); + + // Check required block index + Assert.Empty(inventory.FilterConsumables(row.Id, 3L)); + + // Check insufficient target item count + Assert.False(inventory.RemoveConsumable(row.Id, 6L, 4)); + + // Check remove by block index + Assert.True(inventory.RemoveConsumable(row.Id, 6L)); + Assert.Equal(5L, inventory.Consumables.Min(c => c.RequiredBlockIndex)); + Assert.Equal(2, inventory.Consumables.Count()); + + // Check remove multiple consumable + Assert.True(inventory.RemoveConsumable(row.Id, 6L, 2)); + Assert.Empty(inventory.Items); + } + private static Consumable GetFirstConsumable() { var row = TableSheets.ConsumableItemSheet.First; diff --git a/Lib9c/Model/Item/Inventory.cs b/Lib9c/Model/Item/Inventory.cs index 75920f1e5c..edfe3a6175 100644 --- a/Lib9c/Model/Item/Inventory.cs +++ b/Lib9c/Model/Item/Inventory.cs @@ -492,6 +492,39 @@ public bool RemoveMaterial(int id, long blockIndex, int count = 1) return true; } + public List FilterConsumables(int id, long blockIndex) + { + var consumables = Items.Where(i => !i.Locked && i.item.Id == id) + .Select(i => i.item) + .OfType(); + return consumables + .Where(consumable => consumable.RequiredBlockIndex <= blockIndex) + .OrderBy(c => c.RequiredBlockIndex) + .ThenBy(c => c.NonFungibleId) + .ToList(); + } + + public bool RemoveConsumable(int id, long blockIndex, int count = 1) + { + var consumableItems = FilterConsumables(id, blockIndex); + + var isSufficientItems = consumableItems.Count >= count; + if (!isSufficientItems) + { + return false; + } + + foreach (var consumable in consumableItems.Take(count)) + { + if (!RemoveNonFungibleItem(consumable)) + { + return false; + } + } + + return true; + } + [Obsolete("Use RemoveFungibleItem")] public bool RemoveFungibleItem2( IFungibleItem fungibleItem, From 11ffb830cbc441ed5c5877e13c4f7dbc846699da Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Tue, 6 Feb 2024 11:25:26 +0900 Subject: [PATCH 22/64] Introduce BurnMaterial for remove inventory items --- .../FungibleCollectionMaterialTest.cs | 110 ++++++++++++++++++ .../NonFungibleCollectionMaterialTest.cs | 94 +++++++++++++++ .../Collection/FungibleCollectionMaterial.cs | 34 ++++++ .../NonFungibleCollectionMaterial.cs | 33 ++++++ 4 files changed, 271 insertions(+) create mode 100644 .Lib9c.Tests/Model/Collection/FungibleCollectionMaterialTest.cs create mode 100644 .Lib9c.Tests/Model/Collection/NonFungibleCollectionMaterialTest.cs diff --git a/.Lib9c.Tests/Model/Collection/FungibleCollectionMaterialTest.cs b/.Lib9c.Tests/Model/Collection/FungibleCollectionMaterialTest.cs new file mode 100644 index 0000000000..b31abfcb48 --- /dev/null +++ b/.Lib9c.Tests/Model/Collection/FungibleCollectionMaterialTest.cs @@ -0,0 +1,110 @@ +namespace Lib9c.Tests.Model.Collection +{ + using System; + using System.Linq; + using Lib9c.Tests.Action; + using Nekoyume.Action; + using Nekoyume.Model.Collection; + using Nekoyume.Model.Item; + using Xunit; + + public class FungibleCollectionMaterialTest + { + private readonly TableSheets _tableSheets = new (TableSheetsImporter.ImportSheets()); + + [Theory] + [InlineData(ItemType.Consumable)] + [InlineData(ItemType.Material)] + public void BurnMaterial(ItemType itemType) + { + var inventory = new Inventory(); + var row = _tableSheets.ItemSheet.Values.First(r => r.ItemType == itemType); + var item = ItemFactory.CreateItem(row, new TestRandom()); + inventory.AddItem(item); + Assert.Single(inventory.Items); + var fungibleMaterial = new FungibleCollectionMaterial + { + ItemId = row.Id, + ItemCount = 1, + }; + fungibleMaterial.BurnMaterial(row, inventory, 0L); + Assert.Empty(inventory.Items); + } + + [Theory] + [InlineData(0L, 1L, false)] + [InlineData(1L, 0L, true)] + public void BurnMaterial_Consumable_ItemDoesNotExistException(long requiredBlockIndex, long blockIndex, bool add) + { + var inventory = new Inventory(); + var row = _tableSheets.ItemSheet.Values.First(r => r.ItemType == ItemType.Consumable); + var item = ItemFactory.CreateItemUsable(row, Guid.NewGuid(), requiredBlockIndex); + if (add) + { + inventory.AddItem(item); + Assert.Single(inventory.Items); + } + else + { + Assert.Empty(inventory.Items); + } + + var fungibleMaterial = new FungibleCollectionMaterial + { + ItemId = row.Id, + ItemCount = 1, + }; + Assert.Throws(() => fungibleMaterial.BurnMaterial(row, inventory, blockIndex)); + } + + [Fact] + public void BurnMaterial_Material_ItemDoesNotExistException() + { + var inventory = new Inventory(); + var row = _tableSheets.MaterialItemSheet.Values.First(r => r.ItemType == ItemType.Material); + var tradableMaterial = ItemFactory.CreateTradableMaterial(row); + tradableMaterial.RequiredBlockIndex = 1L; + inventory.AddItem(tradableMaterial); + Assert.Single(inventory.Items); + + var fungibleMaterial = new FungibleCollectionMaterial + { + ItemId = row.Id, + ItemCount = 1, + }; + + // required block index + Assert.Throws(() => fungibleMaterial.BurnMaterial(row, inventory, 0L)); + + // insufficient count + fungibleMaterial.ItemCount = 2; + Assert.Throws(() => fungibleMaterial.BurnMaterial(row, inventory, 1L)); + + var material = ItemFactory.CreateMaterial(row); + inventory.AddItem(material); + Assert.Equal(2, inventory.Items.Count); + + // required block index + Assert.Throws(() => fungibleMaterial.BurnMaterial(row, inventory, 0L)); + } + + [Theory] + [InlineData(ItemType.Costume)] + [InlineData(ItemType.Equipment)] + public void BurnMaterial_InvalidItemTypeException(ItemType itemType) + { + var inventory = new Inventory(); + var row = _tableSheets.ItemSheet.Values.First(r => r.ItemType == itemType); + var item = ItemFactory.CreateItem(row, new TestRandom()); + inventory.AddItem(item); + Assert.Single(inventory.Items); + + var fungibleMaterial = new FungibleCollectionMaterial + { + ItemId = row.Id, + ItemCount = 1, + }; + Assert.Throws(() => fungibleMaterial.BurnMaterial(row, inventory, 0L)); + } + } +} diff --git a/.Lib9c.Tests/Model/Collection/NonFungibleCollectionMaterialTest.cs b/.Lib9c.Tests/Model/Collection/NonFungibleCollectionMaterialTest.cs new file mode 100644 index 0000000000..941ada91b5 --- /dev/null +++ b/.Lib9c.Tests/Model/Collection/NonFungibleCollectionMaterialTest.cs @@ -0,0 +1,94 @@ +namespace Lib9c.Tests.Model.Collection +{ + using System.Linq; + using Lib9c.Tests.Action; + using Nekoyume.Action; + using Nekoyume.Model.Collection; + using Nekoyume.Model.Item; + using Nekoyume.TableData; + using Xunit; + + public class NonFungibleCollectionMaterialTest + { + private readonly TableSheets _tableSheets = new (TableSheetsImporter.ImportSheets()); + + [Theory] + [InlineData(ItemType.Equipment)] + [InlineData(ItemType.Costume)] + public void BurnMaterial(ItemType itemType) + { + var inventory = new Inventory(); + var row = _tableSheets.ItemSheet.Values.First(r => r.ItemType == itemType); + var materialInfo = new CollectionSheet.CollectionMaterial + { + ItemId = row.Id, + Count = 1, + Level = 0, + SkillContains = false, + }; + var item = ItemFactory.CreateItem(row, new TestRandom()); + var nonfungibleId = ((INonFungibleItem)item).NonFungibleId; + inventory.AddItem(item); + Assert.Single(inventory.Items); + var nonFungibleCollectionMaterial = new NonFungibleCollectionMaterial + { + ItemId = row.Id, + ItemCount = 1, + NonFungibleId = nonfungibleId, + }; + nonFungibleCollectionMaterial.BurnMaterial(row, inventory, materialInfo); + Assert.Empty(inventory.Items); + } + + [Theory] + [InlineData(ItemType.Equipment)] + [InlineData(ItemType.Costume)] + public void BurnMaterial_ItemDoesNotExistException(ItemType itemType) + { + var inventory = new Inventory(); + var row = _tableSheets.ItemSheet.Values.First(r => r.ItemType == itemType); + var materialInfo = new CollectionSheet.CollectionMaterial + { + ItemId = row.Id, + Count = 1, + Level = 1, + SkillContains = false, + }; + var item = ItemFactory.CreateItem(row, new TestRandom()); + inventory.AddItem(item); + Assert.Single(inventory.Items); + var fungibleMaterial = new NonFungibleCollectionMaterial + { + ItemId = row.Id, + ItemCount = 1, + }; + Assert.Throws(() => fungibleMaterial.BurnMaterial(row, inventory, materialInfo)); + } + + [Theory] + [InlineData(ItemType.Material)] + [InlineData(ItemType.Consumable)] + public void BurnMaterial_InvalidItemTypeException(ItemType itemType) + { + var inventory = new Inventory(); + var row = _tableSheets.ItemSheet.Values.First(r => r.ItemType == itemType); + var item = ItemFactory.CreateItem(row, new TestRandom()); + inventory.AddItem(item); + Assert.Single(inventory.Items); + + var materialInfo = new CollectionSheet.CollectionMaterial + { + ItemId = row.Id, + Count = 1, + Level = 0, + SkillContains = false, + }; + var nonFungibleMaterial = new NonFungibleCollectionMaterial + { + ItemId = row.Id, + ItemCount = 1, + }; + Assert.Throws(() => nonFungibleMaterial.BurnMaterial(row, inventory, materialInfo)); + } + } +} diff --git a/Lib9c/Model/Collection/FungibleCollectionMaterial.cs b/Lib9c/Model/Collection/FungibleCollectionMaterial.cs index d9335efffe..e9842563a2 100644 --- a/Lib9c/Model/Collection/FungibleCollectionMaterial.cs +++ b/Lib9c/Model/Collection/FungibleCollectionMaterial.cs @@ -31,5 +31,39 @@ public FungibleCollectionMaterial(IValue bencoded) : this((List) bencoded) public FungibleCollectionMaterial() { } + + /// + /// Burns the material from the inventory based on the given item row, inventory, and block index. + /// + /// The item row from the item sheet. + /// The inventory object. + /// The block index. + public void BurnMaterial(ItemSheet.Row itemRow, Inventory inventory, long blockIndex) + { + switch (itemRow.ItemType) + { + case ItemType.Consumable: + if (!inventory.RemoveConsumable(ItemId, blockIndex, ItemCount)) + { + throw new ItemDoesNotExistException( + "failed to load consumable from inventory"); + } + + break; + case ItemType.Material: + if (!inventory.RemoveMaterial(ItemId, blockIndex, ItemCount)) + { + throw new ItemDoesNotExistException( + "failed to load material from inventory"); + } + + break; + case ItemType.Costume: + case ItemType.Equipment: + default: + throw new InvalidItemTypeException( + $"{nameof(FungibleCollectionMaterial)} does not support {itemRow.ItemType}"); + } + } } } diff --git a/Lib9c/Model/Collection/NonFungibleCollectionMaterial.cs b/Lib9c/Model/Collection/NonFungibleCollectionMaterial.cs index a450f00fb2..e73ffb8cab 100644 --- a/Lib9c/Model/Collection/NonFungibleCollectionMaterial.cs +++ b/Lib9c/Model/Collection/NonFungibleCollectionMaterial.cs @@ -37,5 +37,38 @@ public NonFungibleCollectionMaterial() { ItemCount = 1; } + + /// + /// Burns the specified material from the inventory based on the item type. + /// + /// The object representing the item. + /// The object representing the player's inventory. + /// The object representing the material info. + /// Thrown when the material item does not exist in the inventory. + /// Thrown when the item type is not supported by . + public void BurnMaterial(ItemSheet.Row itemRow, Inventory inventory, CollectionSheet.CollectionMaterial materialInfo) + { + switch (itemRow.ItemType) + { + case ItemType.Costume: + case ItemType.Equipment: + if (inventory.TryGetNonFungibleItem(NonFungibleId, + out INonFungibleItem materialItem) && materialInfo.Validate(materialItem)) + { + inventory.RemoveNonFungibleItem(materialItem); + } + else + { + throw new ItemDoesNotExistException($"failed to load {itemRow.ItemType}"); + } + + break; + case ItemType.Consumable: + case ItemType.Material: + default: + throw new InvalidItemTypeException( + $"{nameof(NonFungibleCollectionMaterial)} does not support {itemRow.ItemType}"); + } + } } } From 932cd4eb63c7c840e681f9a384a40052537a6502 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Tue, 6 Feb 2024 11:28:13 +0900 Subject: [PATCH 23/64] Introduce GetMaterial for filter ICollectionMaterial --- .Lib9c.Tests/TableData/CollectionSheetTest.cs | 38 +++++++++++++++++++ Lib9c/TableData/CollectionSheet.cs | 22 ++++++++++- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/.Lib9c.Tests/TableData/CollectionSheetTest.cs b/.Lib9c.Tests/TableData/CollectionSheetTest.cs index 707e4bf25b..639e3d39cd 100644 --- a/.Lib9c.Tests/TableData/CollectionSheetTest.cs +++ b/.Lib9c.Tests/TableData/CollectionSheetTest.cs @@ -64,5 +64,43 @@ public void Validate(ItemType itemType) Assert.False(materialInfo.Validate(equipment)); } } + + [Fact] + public void GetMaterial() + { + var collectionMaterials = new List(); + var materials = new List(); + for (int i = 0; i < 2; i++) + { + var itemId = i + 1; + var count = 3 - i; + CollectionSheet.CollectionMaterial collectionMaterial = new () + { + ItemId = itemId, + Count = count, + Level = 0, + SkillContains = false, + }; + collectionMaterials.Add(collectionMaterial); + var material = new FungibleCollectionMaterial + { + ItemId = itemId, + ItemCount = count, + }; + materials.Add(material); + } + + materials.Reverse(); + for (var index = 0; index < collectionMaterials.Count; index++) + { + var collectionMaterial = collectionMaterials[index]; + Assert.Equal(index + 1, collectionMaterial.ItemId); + Assert.Equal(3 - index, collectionMaterial.Count); + var m = collectionMaterial.GetMaterial(materials); + materials.Remove(m); + } + + Assert.Empty(materials); + } } } diff --git a/Lib9c/TableData/CollectionSheet.cs b/Lib9c/TableData/CollectionSheet.cs index 57532d0a6d..b58cf75fe6 100644 --- a/Lib9c/TableData/CollectionSheet.cs +++ b/Lib9c/TableData/CollectionSheet.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; +using Nekoyume.Action; +using Nekoyume.Model.Collection; using Nekoyume.Model.Item; using Nekoyume.Model.Stat; using static Nekoyume.TableData.TableExtensions; @@ -9,7 +11,6 @@ namespace Nekoyume.TableData { public class CollectionSheet : Sheet { - public class CollectionMaterial { public int ItemId; @@ -27,6 +28,25 @@ private bool Validate(Costume costume) { return costume.Id == ItemId; } + + /// + /// Retrieves the object from the given collection of materials based on the item ID and count. + /// + /// The collection of materials to search. + /// The object if found; otherwise, an exception is thrown. + public ICollectionMaterial GetMaterial(IEnumerable materials) + { + var material = materials.FirstOrDefault(m => + m.ItemId == ItemId && m.ItemCount == Count); + if (material is null) + { + throw new InvalidMaterialException( + $"can't find material {ItemId}/{Count}"); + } + + return material; + } + public bool Validate(INonFungibleItem nonFungibleItem) { switch (nonFungibleItem) From 96f2cb5aff382b613eb2920a485267825dd97dcc Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Tue, 6 Feb 2024 11:31:56 +0900 Subject: [PATCH 24/64] Refactor ActivateCollection action and update tests The ActivateCollection action has been refactored to improve code organization and readability. Adjustments have been made to handle material item types consistently, better match collections, and handle potential exceptions. Associated tests have also been updated to reflect these changes. --- .Lib9c.Tests/Action/ActionEvaluationTest.cs | 28 ++++ .Lib9c.Tests/Action/ActivateCollectionTest.cs | 63 +++++++-- Lib9c/Action/ActivateCollection.cs | 128 +++++++++--------- 3 files changed, 138 insertions(+), 81 deletions(-) diff --git a/.Lib9c.Tests/Action/ActionEvaluationTest.cs b/.Lib9c.Tests/Action/ActionEvaluationTest.cs index b30edc0fb1..3a58483705 100644 --- a/.Lib9c.Tests/Action/ActionEvaluationTest.cs +++ b/.Lib9c.Tests/Action/ActionEvaluationTest.cs @@ -11,6 +11,7 @@ namespace Lib9c.Tests.Action using MessagePack.Resolvers; using Nekoyume.Action; using Nekoyume.Helper; + using Nekoyume.Model.Collection; using Nekoyume.Model.Item; using Nekoyume.Model.Market; using Nekoyume.Model.State; @@ -90,6 +91,7 @@ public ActionEvaluationTest() [InlineData(typeof(CreatePledge))] [InlineData(typeof(TransferAssets))] [InlineData(typeof(RuneSummon))] + [InlineData(typeof(ActivateCollection))] public void Serialize_With_MessagePack(Type actionType) { var action = GetAction(actionType); @@ -453,6 +455,32 @@ private ActionBase GetAction(Type type) GroupId = 20001, SummonCount = 10, }, + ActivateCollection _ => new ActivateCollection + { + AvatarAddress = _sender, + CollectionData = + { + ( + 1, + new List + { + new FungibleCollectionMaterial + { + ItemId = 1, + ItemCount = 2, + }, + new NonFungibleCollectionMaterial + { + ItemId = 2, + ItemCount = 3, + NonFungibleId = Guid.NewGuid(), + Level = 1, + SkillContains = true, + }, + } + ), + }, + }, _ => throw new InvalidCastException(), }; } diff --git a/.Lib9c.Tests/Action/ActivateCollectionTest.cs b/.Lib9c.Tests/Action/ActivateCollectionTest.cs index 2983ac1e73..c86971f940 100644 --- a/.Lib9c.Tests/Action/ActivateCollectionTest.cs +++ b/.Lib9c.Tests/Action/ActivateCollectionTest.cs @@ -52,8 +52,19 @@ public ActivateCollectionTest() foreach (var (key, value) in sheets) { + var s = value; + // Fix csv data for test + if (key == nameof(CollectionSheet)) + { + s = + @"id,item_id,count,level,skill,item_id,count,level,skill,item_id,count,level,skill,item_id,count,level,skill,item_id,count,level,skill,item_id,count,level,skill,stat_type,modify_type,modify_value,stat_type,modify_type,modify_value,stat_type,modify_type,modify_value +1,10110000,1,0,,302000,2,,,200000,2,,,40100000,1,,,,,,,,,,,ATK,Add,1,,,,,, +2,10110000,1,0,,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,1,,,,,, +"; + } + _initialState = _initialState - .SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); + .SetLegacyState(Addresses.TableSheet.Derive(key), s.Serialize()); } } @@ -63,28 +74,50 @@ public void Execute() var row = _tableSheets.CollectionSheet.Values.First(); var avatarState = _initialState.GetAvatarState(_avatarAddress); var materials = new List(); + var random = new TestRandom(); foreach (var material in row.Materials) { var itemRow = _tableSheets.ItemSheet[material.ItemId]; - var item = ItemFactory.CreateItem(itemRow, new TestRandom()); - avatarState.inventory.AddItem(item, material.Count); - if (item is ItemUsable itemUsable) + var itemType = itemRow.ItemType; + if (itemType == ItemType.Material) { - materials.Add(new NonFungibleCollectionMaterial + var item = ItemFactory.CreateItem(itemRow, random); + avatarState.inventory.AddItem(item, material.Count); + materials.Add(new FungibleCollectionMaterial { ItemId = item.Id, - NonFungibleId = itemUsable.NonFungibleId, - OptionCount = material.OptionCount, - SkillContains = material.SkillContains, + ItemCount = material.Count, }); } else { - materials.Add(new FungibleCollectionMaterial + for (int i = 0; i < material.Count; i++) { - ItemId = item.Id, - ItemCount = material.Count, - }); + var item = ItemFactory.CreateItem(itemRow, random); + var nonFungibleId = ((INonFungibleItem)item).NonFungibleId; + avatarState.inventory.AddItem(item); + if (item.ItemType != ItemType.Consumable) + { + materials.Add(new NonFungibleCollectionMaterial + { + ItemId = item.Id, + NonFungibleId = nonFungibleId, + SkillContains = material.SkillContains, + }); + } + else + { + // Add consumable material only one. + if (i == 0) + { + materials.Add(new FungibleCollectionMaterial + { + ItemId = item.Id, + ItemCount = material.Count, + }); + } + } + } } } @@ -97,8 +130,10 @@ public void Execute() ActivateCollection activateCollection = new ActivateCollection() { AvatarAddress = _avatarAddress, - CollectionId = row.Id, - Materials = materials, + CollectionData = + { + (row.Id, materials), + }, }; var nextState = activateCollection.Execute(context); diff --git a/Lib9c/Action/ActivateCollection.cs b/Lib9c/Action/ActivateCollection.cs index 42fc8e1c95..028c13de76 100644 --- a/Lib9c/Action/ActivateCollection.cs +++ b/Lib9c/Action/ActivateCollection.cs @@ -8,88 +8,74 @@ using Libplanet.Crypto; using Nekoyume.Extensions; using Nekoyume.Model.Collection; -using Nekoyume.Model.Item; using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.TableData; -using static Lib9c.SerializeKeys; namespace Nekoyume.Action { - [ActionType("activate_collection")] - public class ActivateCollection: GameAction + [ActionType(ActionTypeText)] + public class ActivateCollection : GameAction { + private const string ActionTypeText = "activate_collection"; + private const int MaxCollectionDataCount = 10; public Address AvatarAddress; - public int CollectionId; - public List Materials = new(); + + public List<(int collectionId, List materials)> CollectionData = new(); + public override IWorld Execute(IActionContext context) { context.UseGas(1); + if (CollectionData.Count > MaxCollectionDataCount) + { + throw new ArgumentOutOfRangeException( + nameof(CollectionData), + CollectionData.Count, + $"CollectionData count exceeds the {MaxCollectionDataCount}"); + } var states = context.PreviousState; if (states.TryGetAvatarState(context.Signer, AvatarAddress, out var avatarState)) { - var sheets = states.GetSheets(containItemSheet: true, sheetTypes: new[] - { - typeof(CollectionSheet) - }); + var sheets = states.GetSheets(containItemSheet: true, + sheetTypes: new[] + { + typeof(CollectionSheet), + }); var collectionSheet = sheets.GetSheet(); - var row = collectionSheet[CollectionId]; - var materials = Materials; - foreach (var materialInfo in row.Materials) + var collectionState = states.TryGetCollectionState(AvatarAddress, out var state) + ? state + : new CollectionState(); + var itemSheet = sheets.GetItemSheet(); + foreach (var (collectionId, materials) in CollectionData) { - var material = materials.FirstOrDefault(m => - m.ItemId == materialInfo.ItemId && m.ItemCount == materialInfo.Count); - if (material is null) + var row = collectionSheet[collectionId]; + foreach (var materialInfo in row.Materials) { - throw new Exception(); + ICollectionMaterial material = materialInfo.GetMaterial(materials); + ItemSheet.Row itemRow = itemSheet[material.ItemId]; + switch (material) + { + case FungibleCollectionMaterial fungibleCollectionMaterial: + fungibleCollectionMaterial.BurnMaterial(itemRow, avatarState.inventory, context.BlockIndex); + break; + case NonFungibleCollectionMaterial nonFungibleCollectionMaterial: + nonFungibleCollectionMaterial.BurnMaterial(itemRow, avatarState.inventory, materialInfo); + break; + default: + throw new ArgumentOutOfRangeException(nameof(material)); + } + + materials.Remove(material); } - switch (material) + + if (materials.Any()) { - case FungibleCollectionMaterial fungibleCollectionMaterial: - if (!avatarState.inventory.RemoveMaterial(materialInfo.ItemId, - materialInfo.Count)) - { - throw new Exception(); - } - break; - case NonFungibleCollectionMaterial nonFungibleCollectionMaterial: - var nonFungibleId = nonFungibleCollectionMaterial.NonFungibleId; - if (avatarState.inventory.TryGetNonFungibleItem(nonFungibleId, - out ItemUsable materialItem) && materialInfo.Validate(materialItem)) - { - avatarState.inventory.RemoveNonFungibleItem(materialItem); - } - else - { - throw new Exception(); - } - break; - default: - throw new ArgumentOutOfRangeException(nameof(material)); + throw new ArgumentOutOfRangeException( + $"material does not match collection {row.Id}"); } - materials.Remove(material); + collectionState.Ids.Add(collectionId); } - - if (materials.Any()) - { - throw new Exception(); - } - - CollectionState collectionState; - try - { - collectionState = states.GetCollectionState(AvatarAddress); - } - catch (FailedLoadStateException) - { - collectionState = new CollectionState(); - } - catch (InvalidCastException) - { - collectionState = new CollectionState(); - } - collectionState.Ids.Add(CollectionId); return states .SetAvatarState(AvatarAddress, avatarState, false, true, false, false) .SetCollectionState(AvatarAddress, collectionState); @@ -102,17 +88,25 @@ public override IWorld Execute(IActionContext context) new Dictionary { ["a"] = AvatarAddress.Serialize(), - ["c"] = (Integer)CollectionId, - ["m"] = new List(Materials.Select(i => i.Serialize())), + ["c"] = new List( + CollectionData + .Select(c => List.Empty + .Add((Integer) c.collectionId) + .Add(new List(c.materials.Select(i => i.Bencoded)))) + ), }.ToImmutableDictionary(); - protected override void LoadPlainValueInternal(IImmutableDictionary plainValue) + + protected override void LoadPlainValueInternal( + IImmutableDictionary plainValue) { AvatarAddress = plainValue["a"].ToAddress(); - CollectionId = (Integer)plainValue["c"]; - var list = (List) plainValue["m"]; - foreach (var innerList in list) + var list = (List) plainValue["c"]; + foreach (var value in list) { - Materials.Add(CollectionFactory.DeserializeMaterial((List)innerList)); + var innerList = (List) value; + CollectionData.Add(((Integer) innerList[0], + ((List) innerList[1]).Select(i => CollectionFactory.DeserializeMaterial((List) i)) + .ToList())); } } } From a29626863f157c922ec1b2d1a5c940ca4ef972fb Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Tue, 6 Feb 2024 23:01:26 +0900 Subject: [PATCH 25/64] Fix broken test --- .Lib9c.Tests/Action/ActivateCollectionTest.cs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/.Lib9c.Tests/Action/ActivateCollectionTest.cs b/.Lib9c.Tests/Action/ActivateCollectionTest.cs index c86971f940..e51d4fbb7f 100644 --- a/.Lib9c.Tests/Action/ActivateCollectionTest.cs +++ b/.Lib9c.Tests/Action/ActivateCollectionTest.cs @@ -24,6 +24,11 @@ public class ActivateCollectionTest public ActivateCollectionTest() { var sheets = TableSheetsImporter.ImportSheets(); + // Fix csv data for test + sheets[nameof(CollectionSheet)] = @"id,item_id,count,level,skill,item_id,count,level,skill,item_id,count,level,skill,item_id,count,level,skill,item_id,count,level,skill,item_id,count,level,skill,stat_type,modify_type,modify_value,stat_type,modify_type,modify_value,stat_type,modify_type,modify_value +1,10110000,1,0,,302000,2,,,200000,2,,,40100000,1,,,,,,,,,,,ATK,Add,1,,,,,, +2,10110000,1,0,,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,1,,,,,,"; + _tableSheets = new TableSheets(sheets); var privateKey = new PrivateKey(); @@ -52,19 +57,8 @@ public ActivateCollectionTest() foreach (var (key, value) in sheets) { - var s = value; - // Fix csv data for test - if (key == nameof(CollectionSheet)) - { - s = - @"id,item_id,count,level,skill,item_id,count,level,skill,item_id,count,level,skill,item_id,count,level,skill,item_id,count,level,skill,item_id,count,level,skill,stat_type,modify_type,modify_value,stat_type,modify_type,modify_value,stat_type,modify_type,modify_value -1,10110000,1,0,,302000,2,,,200000,2,,,40100000,1,,,,,,,,,,,ATK,Add,1,,,,,, -2,10110000,1,0,,,,,,,,,,,,,,,,,,,,,,ATK,Percentage,1,,,,,, -"; - } - _initialState = _initialState - .SetLegacyState(Addresses.TableSheet.Derive(key), s.Serialize()); + .SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); } } From 993f06bf585632f2cf47a2a6b724b0ece5661a4c Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Tue, 6 Feb 2024 23:12:17 +0900 Subject: [PATCH 26/64] Apply Review suggestions --- .../NonFungibleCollectionMaterialTest.cs | 6 +++--- .Lib9c.Tests/TableData/CollectionSheetTest.cs | 8 ++++---- Lib9c/Action/ActivateCollection.cs | 19 ++++++++++--------- .../NonFungibleCollectionMaterial.cs | 4 ++-- Lib9c/TableData/CollectionSheet.cs | 6 +++--- 5 files changed, 22 insertions(+), 21 deletions(-) diff --git a/.Lib9c.Tests/Model/Collection/NonFungibleCollectionMaterialTest.cs b/.Lib9c.Tests/Model/Collection/NonFungibleCollectionMaterialTest.cs index 941ada91b5..c27dd109ab 100644 --- a/.Lib9c.Tests/Model/Collection/NonFungibleCollectionMaterialTest.cs +++ b/.Lib9c.Tests/Model/Collection/NonFungibleCollectionMaterialTest.cs @@ -19,7 +19,7 @@ public void BurnMaterial(ItemType itemType) { var inventory = new Inventory(); var row = _tableSheets.ItemSheet.Values.First(r => r.ItemType == itemType); - var materialInfo = new CollectionSheet.CollectionMaterial + var materialInfo = new CollectionSheet.RequiredMaterial { ItemId = row.Id, Count = 1, @@ -47,7 +47,7 @@ public void BurnMaterial_ItemDoesNotExistException(ItemType itemType) { var inventory = new Inventory(); var row = _tableSheets.ItemSheet.Values.First(r => r.ItemType == itemType); - var materialInfo = new CollectionSheet.CollectionMaterial + var materialInfo = new CollectionSheet.RequiredMaterial { ItemId = row.Id, Count = 1, @@ -76,7 +76,7 @@ public void BurnMaterial_InvalidItemTypeException(ItemType itemType) inventory.AddItem(item); Assert.Single(inventory.Items); - var materialInfo = new CollectionSheet.CollectionMaterial + var materialInfo = new CollectionSheet.RequiredMaterial { ItemId = row.Id, Count = 1, diff --git a/.Lib9c.Tests/TableData/CollectionSheetTest.cs b/.Lib9c.Tests/TableData/CollectionSheetTest.cs index 639e3d39cd..70abe33add 100644 --- a/.Lib9c.Tests/TableData/CollectionSheetTest.cs +++ b/.Lib9c.Tests/TableData/CollectionSheetTest.cs @@ -50,7 +50,7 @@ public void Validate(ItemType itemType) { var row = new TableSheets(TableSheetsImporter.ImportSheets()).ItemSheet.Values.First(r => r.ItemType == itemType); var item = ItemFactory.CreateItem(row, new TestRandom()); - var materialInfo = new CollectionSheet.CollectionMaterial + var materialInfo = new CollectionSheet.RequiredMaterial { ItemId = row.Id, Count = 1, @@ -68,20 +68,20 @@ public void Validate(ItemType itemType) [Fact] public void GetMaterial() { - var collectionMaterials = new List(); + var collectionMaterials = new List(); var materials = new List(); for (int i = 0; i < 2; i++) { var itemId = i + 1; var count = 3 - i; - CollectionSheet.CollectionMaterial collectionMaterial = new () + CollectionSheet.RequiredMaterial requiredMaterial = new () { ItemId = itemId, Count = count, Level = 0, SkillContains = false, }; - collectionMaterials.Add(collectionMaterial); + collectionMaterials.Add(requiredMaterial); var material = new FungibleCollectionMaterial { ItemId = itemId, diff --git a/Lib9c/Action/ActivateCollection.cs b/Lib9c/Action/ActivateCollection.cs index 028c13de76..a3abcd330a 100644 --- a/Lib9c/Action/ActivateCollection.cs +++ b/Lib9c/Action/ActivateCollection.cs @@ -33,6 +33,7 @@ public override IWorld Execute(IActionContext context) CollectionData.Count, $"CollectionData count exceeds the {MaxCollectionDataCount}"); } + var states = context.PreviousState; if (states.TryGetAvatarState(context.Signer, AvatarAddress, out var avatarState)) { @@ -46,29 +47,29 @@ public override IWorld Execute(IActionContext context) ? state : new CollectionState(); var itemSheet = sheets.GetItemSheet(); - foreach (var (collectionId, materials) in CollectionData) + foreach (var (collectionId, collectionMaterials) in CollectionData) { var row = collectionSheet[collectionId]; - foreach (var materialInfo in row.Materials) + foreach (var requiredMaterial in row.Materials) { - ICollectionMaterial material = materialInfo.GetMaterial(materials); - ItemSheet.Row itemRow = itemSheet[material.ItemId]; - switch (material) + ICollectionMaterial registeredMaterial = requiredMaterial.GetMaterial(collectionMaterials); + ItemSheet.Row itemRow = itemSheet[registeredMaterial.ItemId]; + switch (registeredMaterial) { case FungibleCollectionMaterial fungibleCollectionMaterial: fungibleCollectionMaterial.BurnMaterial(itemRow, avatarState.inventory, context.BlockIndex); break; case NonFungibleCollectionMaterial nonFungibleCollectionMaterial: - nonFungibleCollectionMaterial.BurnMaterial(itemRow, avatarState.inventory, materialInfo); + nonFungibleCollectionMaterial.BurnMaterial(itemRow, avatarState.inventory, requiredMaterial); break; default: - throw new ArgumentOutOfRangeException(nameof(material)); + throw new ArgumentOutOfRangeException(nameof(registeredMaterial)); } - materials.Remove(material); + collectionMaterials.Remove(registeredMaterial); } - if (materials.Any()) + if (collectionMaterials.Any()) { throw new ArgumentOutOfRangeException( $"material does not match collection {row.Id}"); diff --git a/Lib9c/Model/Collection/NonFungibleCollectionMaterial.cs b/Lib9c/Model/Collection/NonFungibleCollectionMaterial.cs index e73ffb8cab..ef16993881 100644 --- a/Lib9c/Model/Collection/NonFungibleCollectionMaterial.cs +++ b/Lib9c/Model/Collection/NonFungibleCollectionMaterial.cs @@ -43,10 +43,10 @@ public NonFungibleCollectionMaterial() ///
/// The object representing the item. /// The object representing the player's inventory. - /// The object representing the material info. + /// The object representing the material info. /// Thrown when the material item does not exist in the inventory. /// Thrown when the item type is not supported by . - public void BurnMaterial(ItemSheet.Row itemRow, Inventory inventory, CollectionSheet.CollectionMaterial materialInfo) + public void BurnMaterial(ItemSheet.Row itemRow, Inventory inventory, CollectionSheet.RequiredMaterial materialInfo) { switch (itemRow.ItemType) { diff --git a/Lib9c/TableData/CollectionSheet.cs b/Lib9c/TableData/CollectionSheet.cs index b58cf75fe6..6f467a6430 100644 --- a/Lib9c/TableData/CollectionSheet.cs +++ b/Lib9c/TableData/CollectionSheet.cs @@ -11,7 +11,7 @@ namespace Nekoyume.TableData { public class CollectionSheet : Sheet { - public class CollectionMaterial + public class RequiredMaterial { public int ItemId; public int Count; @@ -67,7 +67,7 @@ public class Row : SheetRow public int Id { get; private set; } - public List Materials = new(); + public List Materials = new(); public List StatModifiers = new(); public override void Set(IReadOnlyList fields) @@ -80,7 +80,7 @@ public override void Set(IReadOnlyList fields) { continue; } - Materials.Add(new CollectionMaterial + Materials.Add(new RequiredMaterial { ItemId = itemId, Count = ParseInt(fields[2 + offset]), From 2e7cb67ebd130e2ecfe2638f166b99127158adbf Mon Sep 17 00:00:00 2001 From: hyeon Date: Wed, 7 Feb 2024 09:51:11 +0900 Subject: [PATCH 27/64] Change SPD calculating logic If a skill is used in this turn, multiply 0.9 instead of 0.6 for default. This change makes faster returning turn for player. --- Lib9c/Arena/ArenaSimulator.cs | 2 +- Lib9c/Battle/RaidSimulator.cs | 2 +- Lib9c/Battle/StageSimulator.cs | 2 +- Lib9c/Model/Character/ArenaCharacter.cs | 4 ++-- Lib9c/Model/Character/CharacterBase.cs | 14 ++++++++------ 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Lib9c/Arena/ArenaSimulator.cs b/Lib9c/Arena/ArenaSimulator.cs index 4388a1462d..f06cd8ca8c 100644 --- a/Lib9c/Arena/ArenaSimulator.cs +++ b/Lib9c/Arena/ArenaSimulator.cs @@ -74,7 +74,7 @@ public ArenaLog Simulate( foreach (var other in players) { var current = players.GetPriority(other); - var speed = current * 0.6m; + var speed = current * (other.usedSkill != null ? 0.9m : 0.6m); players.UpdatePriority(other, speed); } diff --git a/Lib9c/Battle/RaidSimulator.cs b/Lib9c/Battle/RaidSimulator.cs index 4dbeb528bf..475cab4aec 100644 --- a/Lib9c/Battle/RaidSimulator.cs +++ b/Lib9c/Battle/RaidSimulator.cs @@ -160,7 +160,7 @@ public BattleLog Simulate() foreach (var other in Characters) { var current = Characters.GetPriority(other); - var speed = current * 0.6m; + var speed = current * (other == Player && other.usedSkill != null ? 0.9m : 0.6m); Characters.UpdatePriority(other, speed); } diff --git a/Lib9c/Battle/StageSimulator.cs b/Lib9c/Battle/StageSimulator.cs index 216e8f0be1..44a8cc3a2c 100644 --- a/Lib9c/Battle/StageSimulator.cs +++ b/Lib9c/Battle/StageSimulator.cs @@ -233,7 +233,7 @@ public Player Simulate() foreach (var other in Characters) { var current = Characters.GetPriority(other); - var speed = current * 0.6m; + var speed = current * (other == Player && other.usedSkill != null ? 0.9m : 0.6m); Characters.UpdatePriority(other, speed); } diff --git a/Lib9c/Model/Character/ArenaCharacter.cs b/Lib9c/Model/Character/ArenaCharacter.cs index 0edd1ae786..0fbf256598 100644 --- a/Lib9c/Model/Character/ArenaCharacter.cs +++ b/Lib9c/Model/Character/ArenaCharacter.cs @@ -38,6 +38,7 @@ public class ArenaCharacter : ICloneable private ArenaCharacter _target; private int _attackCount; + public ArenaSkill usedSkill; public Guid Id { get; } = Guid.NewGuid(); public BattleStatus.Arena.ArenaSkill SkillLog { get; private set; } @@ -563,7 +564,6 @@ private void Act() ReduceDurationOfBuffs(); ReduceSkillCooldown(); - ArenaSkill usedSkill; if (OnPreSkill()) { usedSkill = new ArenaTick((ArenaCharacter)Clone()); @@ -703,7 +703,7 @@ private BattleStatus.Arena.ArenaSkill UseSkill() var selectedRuneSkill = _runeSkills.SelectWithoutDefaultAttack(_simulator.Random); var selectedSkill = selectedRuneSkill ?? _skills.Select(_simulator.Random); - var usedSkill = selectedSkill.Use( + usedSkill = selectedSkill.Use( this, _target, _simulator.Turn, diff --git a/Lib9c/Model/Character/CharacterBase.cs b/Lib9c/Model/Character/CharacterBase.cs index 8f899fd5da..79d45e8134 100644 --- a/Lib9c/Model/Character/CharacterBase.cs +++ b/Lib9c/Model/Character/CharacterBase.cs @@ -73,6 +73,7 @@ public long CurrentHP public int AttackCount { get; set; } public int AttackCountMax { get; protected set; } + public BattleStatus.Skill usedSkill { get; set; } protected CharacterBase(Simulator simulator, CharacterSheet characterSheet, int characterId, int level, IEnumerable optionalStatModifiers = null) @@ -240,7 +241,7 @@ protected virtual BattleStatus.Skill UseSkill() { var selectedSkill = Skills.Select(Simulator.Random); bool log = Simulator.LogEvent; - var usedSkill = selectedSkill.Use( + usedSkill = selectedSkill.Use( this, Simulator.WaveTurn, BuffFactory.GetBuffs( @@ -272,7 +273,7 @@ private BattleStatus.Skill UseSkillV1() { var selectedSkill = Skills.SelectV1(Simulator.Random); - var usedSkill = selectedSkill.Use( + usedSkill = selectedSkill.Use( this, Simulator.WaveTurn, BuffFactory.GetBuffs( @@ -296,7 +297,7 @@ private BattleStatus.Skill UseSkillV2() { var selectedSkill = Skills.SelectV2(Simulator.Random); - var usedSkill = selectedSkill.Use( + usedSkill = selectedSkill.Use( this, Simulator.WaveTurn, BuffFactory.GetBuffs( @@ -493,6 +494,7 @@ public void Heal(long heal) protected virtual void SetSkill() { + usedSkill = null; if (!Simulator.SkillSheet.TryGetValue(GameConfig.DefaultAttackId, out var skillRow)) { throw new KeyNotFoundException(GameConfig.DefaultAttackId.ToString(CultureInfo.InvariantCulture)); @@ -509,11 +511,11 @@ public bool GetChance(int chance) private void Act() { + usedSkill = null; if (IsAlive()) { ReduceDurationOfBuffs(); ReduceSkillCooldown(); - BattleStatus.Skill usedSkill; if (OnPreSkill()) { usedSkill = new Tick((CharacterBase)Clone()); @@ -541,7 +543,7 @@ private void ActV1() ReduceDurationOfBuffs(); ReduceSkillCooldownV1(); OnPreSkill(); - var usedSkill = UseSkillV1(); + usedSkill = UseSkillV1(); if (usedSkill != null) { OnPostSkill(usedSkill); @@ -559,7 +561,7 @@ private void ActV2() ReduceDurationOfBuffs(); ReduceSkillCooldownV1(); OnPreSkill(); - var usedSkill = UseSkillV2(); + usedSkill = UseSkillV2(); if (usedSkill != null) { OnPostSkill(usedSkill); From c5819db9eaa25b00050e1a89b433e9003ec4bbdd Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Tue, 6 Feb 2024 17:22:40 +0900 Subject: [PATCH 28/64] Calculate collection stats order --- .Lib9c.Tests/Model/PlayerTest.cs | 67 ++++++++++++++++++++++++++---- Lib9c/Model/Stat/CharacterStats.cs | 8 ++-- 2 files changed, 64 insertions(+), 11 deletions(-) diff --git a/.Lib9c.Tests/Model/PlayerTest.cs b/.Lib9c.Tests/Model/PlayerTest.cs index 4e35f6252c..252d946498 100644 --- a/.Lib9c.Tests/Model/PlayerTest.cs +++ b/.Lib9c.Tests/Model/PlayerTest.cs @@ -675,30 +675,83 @@ public void Vampiric(int duration, int percent) } [Fact] - public void SetCollectionStatsTest() + public void StatsLayerTest() { - var row = _tableSheets.EquipmentItemSheet.Values.First(r => r.Stat.StatType == StatType.HP); - var costume = (Equipment)ItemFactory.CreateItem(_tableSheets.ItemSheet[row.Id], new TestRandom()); + var row = _tableSheets.EquipmentItemSheet.Values.First(r => + r.Stat.StatType == StatType.HP); + var equipment = (Equipment)ItemFactory.CreateItem(_tableSheets.ItemSheet[row.Id], new TestRandom()); + equipment.equipped = true; + _avatarState.inventory.AddItem(equipment); + var costumeStatRow = + _tableSheets.CostumeStatSheet.Values.First(r => r.StatType == StatType.HP); + var costumeId = costumeStatRow.CostumeId; + var costume = ItemFactory.CreateCostume(_tableSheets.CostumeItemSheet[costumeId], Guid.NewGuid()); costume.equipped = true; _avatarState.inventory.AddItem(costume); + var foodRow = + _tableSheets.ConsumableItemSheet.Values.First(r => + r.Stats.Any(s => s.StatType == StatType.HP)); + var food = (Consumable)ItemFactory.CreateItem(foodRow, _random); + _avatarState.inventory.AddItem(food); + // Update equipment stats var player = new Player( _avatarState, _tableSheets.CharacterSheet, _tableSheets.CharacterLevelSheet, _tableSheets.EquipmentItemSetEffectSheet ); + Assert.Equal(player.HP, player.Stats.BaseHP + player.Stats.EquipmentStats.HP); + var equipmentLayerHp = player.HP; - Assert.Equal(row.Stat.BaseValue, player.Stats.EquipmentStats.HP); - Assert.Equal(player.HP, player.Stats.BaseHP + row.Stat.BaseValue); + // Update consumable stats + player.Use(new List + { + food.ItemId, + }); + Assert.Equal(player.HP, equipmentLayerHp + food.Stats.Where(s => s.StatType == StatType.HP).Sum(s => s.BaseValueAsLong)); + var consumableLayerHp = player.HP; + + // Update rune stat + var runeId = 30001; + var runeState = new RuneState(runeId); + runeState.LevelUp(); + Assert.Equal(1, runeState.Level); + + var runeStates = new List + { + runeState, + }; + player.SetRune(runeStates, _tableSheets.RuneOptionSheet, _tableSheets.SkillSheet); + var runeOptionRow = _tableSheets.RuneOptionSheet.Values.First(r => r.RuneId == runeId); + var runeHp = runeOptionRow.LevelOptionMap[1].Stats.Sum(r => r.stat.BaseValueAsLong); + Assert.Equal(player.HP, consumableLayerHp + runeHp); + var runeLayerHp = player.HP; + // Update collection stat var modifiers = new List(); var addModifier = new StatModifier(StatType.HP, StatModifier.OperationType.Add, 100); modifiers.Add(new StatModifier(StatType.HP, StatModifier.OperationType.Percentage, 100)); - modifiers.Add(new StatModifier(StatType.HP, StatModifier.OperationType.Percentage, -100)); modifiers.Add(addModifier); + modifiers.Add( + new StatModifier(StatType.HP, StatModifier.OperationType.Percentage, -100)); player.Stats.SetCollections(modifiers); - Assert.Equal(player.HP, player.Stats.BaseHP + row.Stat.BaseValue + addModifier.Value); + Assert.Equal(player.HP, runeLayerHp + addModifier.Value); + var collectionLayerHp = player.HP; + + // Update optional stats + player.SetCostumeStat(_tableSheets.CostumeStatSheet); + Assert.Equal(player.HP, collectionLayerHp + costumeStatRow.Stat); + var costumeLayerHp = player.HP; + + // Update buff stats + var buffRow = _tableSheets.StatBuffSheet.Values.First(r => + r.StatType == StatType.HP && + r.OperationType == StatModifier.OperationType.Percentage); + var buff = new StatBuff(buffRow); + var modifier = buff.GetModifier(); + player.Stats.SetBuffs(new[] { buff }); + Assert.Equal(player.HP, (long)(costumeLayerHp + modifier.GetModifiedValue(runeLayerHp))); } } } diff --git a/Lib9c/Model/Stat/CharacterStats.cs b/Lib9c/Model/Stat/CharacterStats.cs index 1bfcfaf35b..cb37701e84 100644 --- a/Lib9c/Model/Stat/CharacterStats.cs +++ b/Lib9c/Model/Stat/CharacterStats.cs @@ -23,9 +23,9 @@ public class CharacterStats : Stats, IBaseAndAdditionalStats, ICloneable private readonly Stats _equipmentStats = new Stats(); private readonly Stats _consumableStats = new Stats(); private readonly Stats _runeStats = new Stats(); + private readonly Stats _collectionStats = new Stats(); private readonly Stats _buffStats = new Stats(); private readonly Stats _optionalStats = new Stats(); - private readonly Stats _collectionStats = new Stats(); private readonly List _initialStatModifiers = new List(); private readonly List _equipmentStatModifiers = new List(); @@ -334,7 +334,7 @@ public CharacterStats SetCollections(IEnumerable statModifiers, if (updateImmediate) { - UpdateEquipmentStats(); + UpdateCollectionStats(); } return this; @@ -459,13 +459,13 @@ private void UpdateBuffStats() private void UpdateOptionalStats() { _optionalStats.Set(_optionalStatModifiers, _baseStats, _equipmentStats, _consumableStats, _runeStats, _buffStats); - UpdateCollectionStats(); + UpdateTotalStats(); } private void UpdateCollectionStats() { _collectionStats.Set(_collectionStatModifiers, _baseStats, _equipmentStats, _collectionStats, _runeStats, _buffStats, _optionalStats); - UpdateTotalStats(); + UpdateOptionalStats(); } private void UpdateTotalStats() From a726af7d2d08f7c660746c6d1ef10d1ee5b34f7e Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Tue, 6 Feb 2024 20:26:57 +0900 Subject: [PATCH 29/64] Union stat buff by modifier type --- .Lib9c.Tests/Model/PlayerTest.cs | 91 +++++++++++++++++++++--------- Lib9c/Model/Stat/CharacterStats.cs | 30 +++++++++- 2 files changed, 94 insertions(+), 27 deletions(-) diff --git a/.Lib9c.Tests/Model/PlayerTest.cs b/.Lib9c.Tests/Model/PlayerTest.cs index 252d946498..68ab0ee2fe 100644 --- a/.Lib9c.Tests/Model/PlayerTest.cs +++ b/.Lib9c.Tests/Model/PlayerTest.cs @@ -15,6 +15,7 @@ namespace Lib9c.Tests.Model using Nekoyume.Model.Skill; using Nekoyume.Model.Stat; using Nekoyume.Model.State; + using Nekoyume.TableData; using Priority_Queue; using Xunit; @@ -678,19 +679,19 @@ public void Vampiric(int duration, int percent) public void StatsLayerTest() { var row = _tableSheets.EquipmentItemSheet.Values.First(r => - r.Stat.StatType == StatType.HP); + r.Stat.StatType == StatType.ATK); var equipment = (Equipment)ItemFactory.CreateItem(_tableSheets.ItemSheet[row.Id], new TestRandom()); equipment.equipped = true; _avatarState.inventory.AddItem(equipment); var costumeStatRow = - _tableSheets.CostumeStatSheet.Values.First(r => r.StatType == StatType.HP); + _tableSheets.CostumeStatSheet.Values.First(r => r.StatType == StatType.ATK); var costumeId = costumeStatRow.CostumeId; var costume = ItemFactory.CreateCostume(_tableSheets.CostumeItemSheet[costumeId], Guid.NewGuid()); costume.equipped = true; _avatarState.inventory.AddItem(costume); var foodRow = _tableSheets.ConsumableItemSheet.Values.First(r => - r.Stats.Any(s => s.StatType == StatType.HP)); + r.Stats.Any(s => s.StatType == StatType.ATK)); var food = (Consumable)ItemFactory.CreateItem(foodRow, _random); _avatarState.inventory.AddItem(food); @@ -701,19 +702,19 @@ public void StatsLayerTest() _tableSheets.CharacterLevelSheet, _tableSheets.EquipmentItemSetEffectSheet ); - Assert.Equal(player.HP, player.Stats.BaseHP + player.Stats.EquipmentStats.HP); - var equipmentLayerHp = player.HP; + Assert.Equal(player.ATK, player.Stats.BaseATK + player.Stats.EquipmentStats.ATK); + var equipmentLayerAtk = player.ATK; // Update consumable stats player.Use(new List { food.ItemId, }); - Assert.Equal(player.HP, equipmentLayerHp + food.Stats.Where(s => s.StatType == StatType.HP).Sum(s => s.BaseValueAsLong)); - var consumableLayerHp = player.HP; + Assert.Equal(player.ATK, equipmentLayerAtk + food.Stats.Where(s => s.StatType == StatType.ATK).Sum(s => s.BaseValueAsLong)); + var consumableLayerAtk = player.ATK; // Update rune stat - var runeId = 30001; + var runeId = 10002; var runeState = new RuneState(runeId); runeState.LevelUp(); Assert.Equal(1, runeState.Level); @@ -724,34 +725,72 @@ public void StatsLayerTest() }; player.SetRune(runeStates, _tableSheets.RuneOptionSheet, _tableSheets.SkillSheet); var runeOptionRow = _tableSheets.RuneOptionSheet.Values.First(r => r.RuneId == runeId); - var runeHp = runeOptionRow.LevelOptionMap[1].Stats.Sum(r => r.stat.BaseValueAsLong); - Assert.Equal(player.HP, consumableLayerHp + runeHp); - var runeLayerHp = player.HP; + var runeAtk = runeOptionRow.LevelOptionMap[1].Stats.Sum(r => r.stat.BaseValueAsLong); + Assert.Equal(player.ATK, consumableLayerAtk + runeAtk); + var runeLayerAtk = player.ATK; // Update collection stat var modifiers = new List(); - var addModifier = new StatModifier(StatType.HP, StatModifier.OperationType.Add, 100); - modifiers.Add(new StatModifier(StatType.HP, StatModifier.OperationType.Percentage, 100)); + var addModifier = new StatModifier(StatType.ATK, StatModifier.OperationType.Add, 100); + modifiers.Add(new StatModifier(StatType.ATK, StatModifier.OperationType.Percentage, 100)); modifiers.Add(addModifier); - modifiers.Add( - new StatModifier(StatType.HP, StatModifier.OperationType.Percentage, -100)); + modifiers.Add(new StatModifier(StatType.ATK, StatModifier.OperationType.Percentage, -100)); player.Stats.SetCollections(modifiers); - Assert.Equal(player.HP, runeLayerHp + addModifier.Value); - var collectionLayerHp = player.HP; + Assert.Equal(player.ATK, runeLayerAtk + addModifier.Value); + var collectionLayerAtk = player.ATK; // Update optional stats player.SetCostumeStat(_tableSheets.CostumeStatSheet); - Assert.Equal(player.HP, collectionLayerHp + costumeStatRow.Stat); - var costumeLayerHp = player.HP; + Assert.Equal(player.ATK, collectionLayerAtk + costumeStatRow.Stat); + var costumeLayerAtk = player.ATK; - // Update buff stats - var buffRow = _tableSheets.StatBuffSheet.Values.First(r => - r.StatType == StatType.HP && + // Update stage buff stats + var stageBuffSkill = CrystalRandomSkillState.GetSkill( + 1, + _tableSheets.CrystalRandomBuffSheet, + _tableSheets.SkillSheet); + var stageBuffs = BuffFactory.GetBuffs( + player.Stats, + stageBuffSkill, + _tableSheets.SkillBuffSheet, + _tableSheets.StatBuffSheet, + _tableSheets.SkillActionBuffSheet, + _tableSheets.ActionBuffSheet + ); + var statBuffs = new List(); + + foreach (var stageBuff in stageBuffs) + { + player.AddBuff(stageBuff); + if (stageBuff is StatBuff s) + { + statBuffs.Add(s); + } + } + + StatBuff stageAtkBuff = statBuffs.Single(s => s.GetModifier().StatType == StatType.ATK); + var stageModifier = stageAtkBuff.GetModifier(); + Assert.Equal(player.ATK, (long)(costumeLayerAtk + stageModifier.GetModifiedValue(runeLayerAtk))); + + // Update skill buff stats + var percentageBuffRow = _tableSheets.StatBuffSheet.Values.First(r => + r.StatType == StatType.ATK && r.OperationType == StatModifier.OperationType.Percentage); - var buff = new StatBuff(buffRow); - var modifier = buff.GetModifier(); - player.Stats.SetBuffs(new[] { buff }); - Assert.Equal(player.HP, (long)(costumeLayerHp + modifier.GetModifiedValue(runeLayerHp))); + var percentageBuff = new StatBuff(percentageBuffRow); + statBuffs.Add(percentageBuff); + var percentageModifier = percentageBuff.GetModifier(); + + // Divide buff group id for test + var addBuffRow = new StatBuffSheet.Row(); + addBuffRow.Set("102003,102003,100,10,Self,ATK,Add,10,true\n".Split(",")); + + var addBuff = new StatBuff(addBuffRow); + statBuffs.Add(addBuff); + var addBuffModifier = addBuff.GetModifier(); + + player.Stats.SetBuffs(statBuffs); + var totalBuffPercentageModifier = new StatModifier(StatType.ATK, StatModifier.OperationType.Percentage, stageModifier.Value + percentageModifier.Value); + Assert.Equal(player.ATK, (long)(costumeLayerAtk + addBuffModifier.Value + totalBuffPercentageModifier.GetModifiedValue(runeLayerAtk))); } } } diff --git a/Lib9c/Model/Stat/CharacterStats.cs b/Lib9c/Model/Stat/CharacterStats.cs index cb37701e84..eed69c1611 100644 --- a/Lib9c/Model/Stat/CharacterStats.cs +++ b/Lib9c/Model/Stat/CharacterStats.cs @@ -452,7 +452,35 @@ private void UpdateRuneStats() private void UpdateBuffStats() { - _buffStats.Set(_buffStatModifiers.Values, _baseStats, _equipmentStats, _consumableStats, _runeStats); + var buffModifiers = new List(); + var perModifiers = new List(); + foreach (var modifier in _buffStatModifiers.Values) + { + switch (modifier.Operation) + { + case StatModifier.OperationType.Add: + buffModifiers.Add(modifier); + break; + case StatModifier.OperationType.Percentage: + perModifiers.Add(modifier); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + var groupBy = perModifiers.GroupBy(m => m.StatType).ToList(); + foreach (var group in groupBy) + { + var statType = group.Key; + var sum = group.Sum(g => g.Value); + if (sum > 0L) + { + buffModifiers.Add(new StatModifier(statType, StatModifier.OperationType.Percentage, sum)); + } + } + + _buffStats.Set(buffModifiers, _baseStats, _equipmentStats, _consumableStats, _runeStats); UpdateOptionalStats(); } From a49f6acd815071061577690a44316812b2515e0f Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Tue, 6 Feb 2024 22:31:46 +0900 Subject: [PATCH 30/64] Fix debuff percentage case --- Lib9c/Model/Stat/CharacterStats.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Lib9c/Model/Stat/CharacterStats.cs b/Lib9c/Model/Stat/CharacterStats.cs index eed69c1611..5f822e8fb2 100644 --- a/Lib9c/Model/Stat/CharacterStats.cs +++ b/Lib9c/Model/Stat/CharacterStats.cs @@ -326,10 +326,7 @@ public CharacterStats SetCollections(IEnumerable statModifiers, { var statType = group.Key; var sum = group.Sum(g => g.Value); - if (sum > 0L) - { - _collectionStatModifiers.Add(new StatModifier(statType, StatModifier.OperationType.Percentage, sum)); - } + _collectionStatModifiers.Add(new StatModifier(statType, StatModifier.OperationType.Percentage, sum)); } if (updateImmediate) @@ -474,10 +471,7 @@ private void UpdateBuffStats() { var statType = group.Key; var sum = group.Sum(g => g.Value); - if (sum > 0L) - { - buffModifiers.Add(new StatModifier(statType, StatModifier.OperationType.Percentage, sum)); - } + buffModifiers.Add(new StatModifier(statType, StatModifier.OperationType.Percentage, sum)); } _buffStats.Set(buffModifiers, _baseStats, _equipmentStats, _consumableStats, _runeStats); From 9be35e5d1ea5a7fb62a73cfccbb7689cdc8e09db Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Tue, 6 Feb 2024 22:43:28 +0900 Subject: [PATCH 31/64] Rename OptionStats to CostumStats --- .Lib9c.Tests/Model/PlayerTest.cs | 4 +-- .Lib9c.Tests/Model/RankingSimulatorTest.cs | 6 ++-- .Lib9c.Tests/Model/RankingSimulatorV1Test.cs | 6 ++-- .Lib9c.Tests/Model/StageSimulatorTest.cs | 4 +-- .Lib9c.Tests/Model/StageSimulatorV1Test.cs | 4 +-- Lib9c/Model/Character/ArenaCharacter.cs | 8 ++--- Lib9c/Model/Character/CharacterBase.cs | 2 +- Lib9c/Model/Character/Player.cs | 4 +-- Lib9c/Model/Stat/CharacterStats.cs | 35 +++++++++----------- 9 files changed, 35 insertions(+), 38 deletions(-) diff --git a/.Lib9c.Tests/Model/PlayerTest.cs b/.Lib9c.Tests/Model/PlayerTest.cs index 68ab0ee2fe..4012a663dc 100644 --- a/.Lib9c.Tests/Model/PlayerTest.cs +++ b/.Lib9c.Tests/Model/PlayerTest.cs @@ -289,11 +289,11 @@ public void SetCostumeStat() ); player.SetCostumeStat(_tableSheets.CostumeStatSheet); - Assert.Equal(row.Stat, player.Stats.OptionalStats.ATK); + Assert.Equal(row.Stat, player.Stats.CostumeStats.ATK); var copy = (Player)player.Clone(); - Assert.Equal(row.Stat, copy.Stats.OptionalStats.ATK); + Assert.Equal(row.Stat, copy.Stats.CostumeStats.ATK); } [Fact] diff --git a/.Lib9c.Tests/Model/RankingSimulatorTest.cs b/.Lib9c.Tests/Model/RankingSimulatorTest.cs index e534a35274..0b8b3d6492 100644 --- a/.Lib9c.Tests/Model/RankingSimulatorTest.cs +++ b/.Lib9c.Tests/Model/RankingSimulatorTest.cs @@ -174,14 +174,14 @@ public void ConstructorWithCostume() ); var player = simulator.Player; - Assert.Equal(row.Stat, player.Stats.OptionalStats.ATK); + Assert.Equal(row.Stat, player.Stats.CostumeStats.ATK); var player2 = simulator.Simulate(); - Assert.Equal(row.Stat, player2.Stats.OptionalStats.ATK); + Assert.Equal(row.Stat, player2.Stats.CostumeStats.ATK); var e = simulator.Log.OfType().First(); var enemyPlayer = (EnemyPlayer)e.Character; - Assert.Equal(row2.Stat, enemyPlayer.Stats.OptionalStats.DEF); + Assert.Equal(row2.Stat, enemyPlayer.Stats.CostumeStats.DEF); } [Theory] diff --git a/.Lib9c.Tests/Model/RankingSimulatorV1Test.cs b/.Lib9c.Tests/Model/RankingSimulatorV1Test.cs index ba916fd530..b958cc4cef 100644 --- a/.Lib9c.Tests/Model/RankingSimulatorV1Test.cs +++ b/.Lib9c.Tests/Model/RankingSimulatorV1Test.cs @@ -158,14 +158,14 @@ public void ConstructorWithCostume() ); var player = simulator.Player; - Assert.Equal(row.Stat, player.Stats.OptionalStats.ATK); + Assert.Equal(row.Stat, player.Stats.CostumeStats.ATK); var player2 = simulator.SimulateV2(); - Assert.Equal(row.Stat, player2.Stats.OptionalStats.ATK); + Assert.Equal(row.Stat, player2.Stats.CostumeStats.ATK); var e = simulator.Log.OfType().First(); var enemyPlayer = (EnemyPlayer)e.Character; - Assert.Equal(row2.Stat, enemyPlayer.Stats.OptionalStats.DEF); + Assert.Equal(row2.Stat, enemyPlayer.Stats.CostumeStats.DEF); } [Theory] diff --git a/.Lib9c.Tests/Model/StageSimulatorTest.cs b/.Lib9c.Tests/Model/StageSimulatorTest.cs index 519f0b4dc7..5fd10b9a3c 100644 --- a/.Lib9c.Tests/Model/StageSimulatorTest.cs +++ b/.Lib9c.Tests/Model/StageSimulatorTest.cs @@ -68,7 +68,7 @@ public void Simulate() ); var player = simulator.Player; - Assert.Equal(row.Stat, player.Stats.OptionalStats.ATK); + Assert.Equal(row.Stat, player.Stats.CostumeStats.ATK); Assert.Equal(100, player.Stats.CollectionStats.ATK); Assert.Equal(100 + row.Stat + player.Stats.BaseStats.ATK, player.Stats.ATK); while (player.Level == 1) @@ -77,7 +77,7 @@ public void Simulate() } var player2 = simulator.Player; - Assert.Equal(row.Stat, player2.Stats.OptionalStats.ATK); + Assert.Equal(row.Stat, player2.Stats.CostumeStats.ATK); Assert.Equal(2, player2.Level); Assert.Equal(1, player2.eventMap[(int)QuestEventType.Level]); Assert.True(simulator.Log.OfType().Any()); diff --git a/.Lib9c.Tests/Model/StageSimulatorV1Test.cs b/.Lib9c.Tests/Model/StageSimulatorV1Test.cs index 91728e4f78..90bee1fe1b 100644 --- a/.Lib9c.Tests/Model/StageSimulatorV1Test.cs +++ b/.Lib9c.Tests/Model/StageSimulatorV1Test.cs @@ -72,10 +72,10 @@ public void ConstructorWithCostume() 2); var player = simulator.Player; - Assert.Equal(row.Stat, player.Stats.OptionalStats.ATK); + Assert.Equal(row.Stat, player.Stats.CostumeStats.ATK); var player2 = simulator.SimulateV2(); - Assert.Equal(row.Stat, player2.Stats.OptionalStats.ATK); + Assert.Equal(row.Stat, player2.Stats.CostumeStats.ATK); } [Fact] diff --git a/Lib9c/Model/Character/ArenaCharacter.cs b/Lib9c/Model/Character/ArenaCharacter.cs index 0edd1ae786..64bb7517e0 100644 --- a/Lib9c/Model/Character/ArenaCharacter.cs +++ b/Lib9c/Model/Character/ArenaCharacter.cs @@ -276,7 +276,7 @@ private static CharacterStats GetStat( } } - stats.SetOption(options); + stats.SetCostume(options); return stats; } @@ -301,7 +301,7 @@ private static CharacterStats GetStatV1( } } - stats.SetOption(options); + stats.SetCostume(options); return stats; } @@ -339,7 +339,7 @@ public void SetRuneV1( x.stat.StatType, x.operationType, x.stat.TotalValueAsLong))); - Stats.AddOptional(statModifiers); + Stats.AddCostume(statModifiers); ResetCurrentHP(); if (optionInfo.SkillId == default || @@ -400,7 +400,7 @@ public void SetRuneV2( x.stat.StatType, x.operationType, x.stat.TotalValueAsLong))); - Stats.AddOptional(statModifiers); + Stats.AddCostume(statModifiers); ResetCurrentHP(); if (optionInfo.SkillId == default || diff --git a/Lib9c/Model/Character/CharacterBase.cs b/Lib9c/Model/Character/CharacterBase.cs index 8f899fd5da..a6f5b0f981 100644 --- a/Lib9c/Model/Character/CharacterBase.cs +++ b/Lib9c/Model/Character/CharacterBase.cs @@ -87,7 +87,7 @@ protected CharacterBase(Simulator simulator, CharacterSheet characterSheet, int Stats = new CharacterStats(RowData, level); if (!(optionalStatModifiers is null)) { - Stats.AddOptional(optionalStatModifiers); + Stats.AddCostume(optionalStatModifiers); } ResetCurrentHP(); diff --git a/Lib9c/Model/Character/Player.cs b/Lib9c/Model/Character/Player.cs index 6d17daacc4..ea1a52599f 100644 --- a/Lib9c/Model/Character/Player.cs +++ b/Lib9c/Model/Character/Player.cs @@ -564,7 +564,7 @@ public void SetCostumeStat(CostumeStatSheet costumeStatSheet) .Select(row => new StatModifier(row.StatType, StatModifier.OperationType.Add, (int) row.Stat)) ); } - Stats.SetOption(statModifiers); + Stats.SetCostume(statModifiers); ResetCurrentHP(); } @@ -642,7 +642,7 @@ public void SetRuneV1( x.stat.StatType, x.operationType, x.stat.TotalValueAsLong))); - Stats.AddOptional(statModifiers); + Stats.AddCostume(statModifiers); ResetCurrentHP(); if (optionInfo.SkillId == default || diff --git a/Lib9c/Model/Stat/CharacterStats.cs b/Lib9c/Model/Stat/CharacterStats.cs index 5f822e8fb2..fcd798a6d3 100644 --- a/Lib9c/Model/Stat/CharacterStats.cs +++ b/Lib9c/Model/Stat/CharacterStats.cs @@ -23,17 +23,17 @@ public class CharacterStats : Stats, IBaseAndAdditionalStats, ICloneable private readonly Stats _equipmentStats = new Stats(); private readonly Stats _consumableStats = new Stats(); private readonly Stats _runeStats = new Stats(); + private readonly Stats _costumeStats = new Stats(); private readonly Stats _collectionStats = new Stats(); private readonly Stats _buffStats = new Stats(); - private readonly Stats _optionalStats = new Stats(); private readonly List _initialStatModifiers = new List(); private readonly List _equipmentStatModifiers = new List(); private readonly List _consumableStatModifiers = new List(); private readonly List _runeStatModifiers = new List(); - private readonly Dictionary _buffStatModifiers = new Dictionary(); - private readonly List _optionalStatModifiers = new List(); + private readonly List _costumeStatModifiers = new List(); private readonly List _collectionStatModifiers = new List(); + private readonly Dictionary _buffStatModifiers = new Dictionary(); public readonly StatMap StatWithItems = new StatMap(); @@ -44,7 +44,7 @@ public class CharacterStats : Stats, IBaseAndAdditionalStats, ICloneable public IStats ConsumableStats => _consumableStats; public IStats RuneStats => _runeStats; public IStats BuffStats => _buffStats; - public IStats OptionalStats => _optionalStats; + public IStats CostumeStats => _costumeStats; public IStats CollectionStats => _collectionStats; public long BaseHP => BaseStats.HP; @@ -120,13 +120,13 @@ public CharacterStats(CharacterStats value) : base(value) _consumableStats = new Stats(value._consumableStats); _runeStats = new Stats(value._runeStats); _buffStats = new Stats(value._buffStats); - _optionalStats = new Stats(value._optionalStats); + _costumeStats = new Stats(value._costumeStats); _equipmentStatModifiers = value._equipmentStatModifiers; _consumableStatModifiers = value._consumableStatModifiers; _runeStatModifiers = value._runeStatModifiers; _buffStatModifiers = value._buffStatModifiers; - _optionalStatModifiers = value._optionalStatModifiers; + _costumeStatModifiers = value._costumeStatModifiers; IsArenaCharacter = value.IsArenaCharacter; Level = value.Level; @@ -366,10 +366,10 @@ public void AddRune(IEnumerable statModifiers) UpdateRuneStats(); } - public void AddOptional(IEnumerable statModifiers) + public void AddCostume(IEnumerable statModifiers) { - _optionalStatModifiers.AddRange(statModifiers); - UpdateOptionalStats(); + _costumeStatModifiers.AddRange(statModifiers); + UpdateCostumeStats(); } private void SetCostumes(IEnumerable costumes, CostumeStatSheet costumeStatSheet) @@ -383,13 +383,13 @@ private void SetCostumes(IEnumerable costumes, CostumeStatSheet costume (int)row.Stat)); statModifiers.AddRange(stat); } - SetOption(statModifiers); + SetCostume(statModifiers); } - public void SetOption(IEnumerable statModifiers) + public void SetCostume(IEnumerable statModifiers) { - _optionalStatModifiers.Clear(); - AddOptional(statModifiers); + _costumeStatModifiers.Clear(); + AddCostume(statModifiers); } public void IncreaseHpForArena() @@ -475,24 +475,21 @@ private void UpdateBuffStats() } _buffStats.Set(buffModifiers, _baseStats, _equipmentStats, _consumableStats, _runeStats); - UpdateOptionalStats(); } - private void UpdateOptionalStats() + private void UpdateCostumeStats() { - _optionalStats.Set(_optionalStatModifiers, _baseStats, _equipmentStats, _consumableStats, _runeStats, _buffStats); UpdateTotalStats(); } private void UpdateCollectionStats() { - _collectionStats.Set(_collectionStatModifiers, _baseStats, _equipmentStats, _collectionStats, _runeStats, _buffStats, _optionalStats); - UpdateOptionalStats(); + _collectionStats.Set(_collectionStatModifiers, _baseStats, _equipmentStats, _consumableStats, _runeStats, _costumeStats); } private void UpdateTotalStats() { - Set(_statMap, _baseStats, _equipmentStats, _consumableStats, _runeStats, _buffStats, _optionalStats, _collectionStats); + Set(_statMap, _baseStats, _equipmentStats, _consumableStats, _runeStats, _costumeStats, _collectionStats, _buffStats); foreach (var stat in _statMap.GetDecimalStats(false)) { From 4d076f2b18566ba3ba042beb50cb5057c357f969 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Tue, 6 Feb 2024 22:45:13 +0900 Subject: [PATCH 32/64] Rebalancing stats calculate - buff stats calculate additional costume, collection stats - StatWithItems calculate additional costume, collection stats --- .Lib9c.Tests/Model/PlayerTest.cs | 39 +++++++++++++++----- Lib9c/Model/Stat/CharacterStats.cs | 58 +++++++++++------------------- 2 files changed, 51 insertions(+), 46 deletions(-) diff --git a/.Lib9c.Tests/Model/PlayerTest.cs b/.Lib9c.Tests/Model/PlayerTest.cs index 4012a663dc..65000f14c0 100644 --- a/.Lib9c.Tests/Model/PlayerTest.cs +++ b/.Lib9c.Tests/Model/PlayerTest.cs @@ -703,6 +703,8 @@ public void StatsLayerTest() _tableSheets.EquipmentItemSetEffectSheet ); Assert.Equal(player.ATK, player.Stats.BaseATK + player.Stats.EquipmentStats.ATK); + // BaseAtk 20, EquipmentStats 1 + // Assert.Equal(21, player.ATK); var equipmentLayerAtk = player.ATK; // Update consumable stats @@ -711,6 +713,8 @@ public void StatsLayerTest() food.ItemId, }); Assert.Equal(player.ATK, equipmentLayerAtk + food.Stats.Where(s => s.StatType == StatType.ATK).Sum(s => s.BaseValueAsLong)); + // ConsumableStats 18 + // Assert.Equal(39, player.ATK); var consumableLayerAtk = player.ATK; // Update rune stat @@ -727,8 +731,17 @@ public void StatsLayerTest() var runeOptionRow = _tableSheets.RuneOptionSheet.Values.First(r => r.RuneId == runeId); var runeAtk = runeOptionRow.LevelOptionMap[1].Stats.Sum(r => r.stat.BaseValueAsLong); Assert.Equal(player.ATK, consumableLayerAtk + runeAtk); + // RuneStats 235 + // Assert.Equal(274, player.ATK); var runeLayerAtk = player.ATK; + // Update costume stats + player.SetCostumeStat(_tableSheets.CostumeStatSheet); + Assert.Equal(player.ATK, runeLayerAtk + costumeStatRow.Stat); + // CostumeStats 1829 + // Assert.Equal(2103, player.ATK); + var costumeLayerAtk = player.ATK; + // Update collection stat var modifiers = new List(); var addModifier = new StatModifier(StatType.ATK, StatModifier.OperationType.Add, 100); @@ -736,14 +749,11 @@ public void StatsLayerTest() modifiers.Add(addModifier); modifiers.Add(new StatModifier(StatType.ATK, StatModifier.OperationType.Percentage, -100)); player.Stats.SetCollections(modifiers); - Assert.Equal(player.ATK, runeLayerAtk + addModifier.Value); + Assert.Equal(player.ATK, costumeLayerAtk + addModifier.Value); + // CollectionStats 100 + // Assert.Equal(2203, player.ATK); var collectionLayerAtk = player.ATK; - // Update optional stats - player.SetCostumeStat(_tableSheets.CostumeStatSheet); - Assert.Equal(player.ATK, collectionLayerAtk + costumeStatRow.Stat); - var costumeLayerAtk = player.ATK; - // Update stage buff stats var stageBuffSkill = CrystalRandomSkillState.GetSkill( 1, @@ -770,7 +780,11 @@ public void StatsLayerTest() StatBuff stageAtkBuff = statBuffs.Single(s => s.GetModifier().StatType == StatType.ATK); var stageModifier = stageAtkBuff.GetModifier(); - Assert.Equal(player.ATK, (long)(costumeLayerAtk + stageModifier.GetModifiedValue(runeLayerAtk))); + var stageBuffAtk = (long)stageModifier.GetModifiedValue(collectionLayerAtk); + // StageBuffStats 1101(50%) + // Assert.Equal(1101, stageBuffAtk); + Assert.Equal(player.ATK, collectionLayerAtk + stageBuffAtk); + // Assert.Equal(3304, player.ATK); // Update skill buff stats var percentageBuffRow = _tableSheets.StatBuffSheet.Values.First(r => @@ -779,6 +793,10 @@ public void StatsLayerTest() var percentageBuff = new StatBuff(percentageBuffRow); statBuffs.Add(percentageBuff); var percentageModifier = percentageBuff.GetModifier(); + var totalBuffPercentageModifier = new StatModifier(StatType.ATK, StatModifier.OperationType.Percentage, stageModifier.Value + percentageModifier.Value); + var percentageBuffAtk = (long)totalBuffPercentageModifier.GetModifiedValue(costumeLayerAtk + 100); + // Total PercentageStats 1652(StageBuffStats 50% + BuffStats 25%) + // Assert.Equal(1652, percentageBuffAtk); // Divide buff group id for test var addBuffRow = new StatBuffSheet.Row(); @@ -787,10 +805,13 @@ public void StatsLayerTest() var addBuff = new StatBuff(addBuffRow); statBuffs.Add(addBuff); var addBuffModifier = addBuff.GetModifier(); + var addBuffAtk = addBuffModifier.GetModifiedValue(costumeLayerAtk); + Assert.Equal(10, addBuffAtk); player.Stats.SetBuffs(statBuffs); - var totalBuffPercentageModifier = new StatModifier(StatType.ATK, StatModifier.OperationType.Percentage, stageModifier.Value + percentageModifier.Value); - Assert.Equal(player.ATK, (long)(costumeLayerAtk + addBuffModifier.Value + totalBuffPercentageModifier.GetModifiedValue(runeLayerAtk))); + Assert.Equal(player.ATK, collectionLayerAtk + addBuffAtk + percentageBuffAtk); + // 20 + 1 + 18 + 1829 + 235 + 100 + 1662 + // Assert.Equal(3865, player.ATK); } } } diff --git a/Lib9c/Model/Stat/CharacterStats.cs b/Lib9c/Model/Stat/CharacterStats.cs index fcd798a6d3..7f631fcdfb 100644 --- a/Lib9c/Model/Stat/CharacterStats.cs +++ b/Lib9c/Model/Stat/CharacterStats.cs @@ -132,25 +132,6 @@ public CharacterStats(CharacterStats value) : base(value) Level = value.Level; } - public CharacterStats SetAll( - int level, - IEnumerable equipments, - IEnumerable costumes, - IEnumerable consumables, - IEnumerable runeStats, - EquipmentItemSetEffectSheet equipmentItemSetEffectSheet, - CostumeStatSheet costumeStatSheet) - { - SetStats(level, false); - SetEquipments(equipments, equipmentItemSetEffectSheet, false); - SetCostumes(costumes, costumeStatSheet); - SetConsumables(consumables, false); - SetRunes(runeStats, false); - UpdateBaseStats(); - - return this; - } - /// /// Set base stats based on character level. /// @@ -429,22 +410,7 @@ private void UpdateConsumableStats() private void UpdateRuneStats() { _runeStats.Set(_runeStatModifiers, _baseStats, _equipmentStats, _consumableStats); - Set(StatWithItems, _baseStats, _equipmentStats, _consumableStats, _runeStats); - foreach (var stat in StatWithItems.GetDecimalStats(false)) - { - if (!LegacyDecimalStatTypes.Contains(stat.StatType)) - { - var value = Math.Max(0m, stat.BaseValueAsLong); - stat.SetBaseValue(value); - } - else - { - var value = Math.Max(0m, stat.BaseValue); - stat.SetBaseValue(value); - } - } - - UpdateBuffStats(); + UpdateCostumeStats(); } private void UpdateBuffStats() @@ -474,17 +440,35 @@ private void UpdateBuffStats() buffModifiers.Add(new StatModifier(statType, StatModifier.OperationType.Percentage, sum)); } - _buffStats.Set(buffModifiers, _baseStats, _equipmentStats, _consumableStats, _runeStats); + _buffStats.Set(buffModifiers, _baseStats, _equipmentStats, _consumableStats, _runeStats, _costumeStats, _collectionStats); + UpdateTotalStats(); } private void UpdateCostumeStats() { - UpdateTotalStats(); + _costumeStats.Set(_costumeStatModifiers, _baseStats, _equipmentStats, _consumableStats, _runeStats); + UpdateCollectionStats(); } private void UpdateCollectionStats() { _collectionStats.Set(_collectionStatModifiers, _baseStats, _equipmentStats, _consumableStats, _runeStats, _costumeStats); + Set(StatWithItems, _baseStats, _equipmentStats, _consumableStats, _runeStats, _costumeStats, _collectionStats); + foreach (var stat in StatWithItems.GetDecimalStats(false)) + { + if (!LegacyDecimalStatTypes.Contains(stat.StatType)) + { + var value = Math.Max(0m, stat.BaseValueAsLong); + stat.SetBaseValue(value); + } + else + { + var value = Math.Max(0m, stat.BaseValue); + stat.SetBaseValue(value); + } + } + + UpdateBuffStats(); } private void UpdateTotalStats() From 4b883029dbea49747637ab88a1178382b181c458 Mon Sep 17 00:00:00 2001 From: area363 Date: Wed, 7 Feb 2024 13:30:10 +0900 Subject: [PATCH 33/64] add heimdall internal planet --- Lib9c.Policy/Policy/BlockPolicySource.cs | 6 ++++++ Lib9c/Planet.cs | 1 + 2 files changed, 7 insertions(+) diff --git a/Lib9c.Policy/Policy/BlockPolicySource.cs b/Lib9c.Policy/Policy/BlockPolicySource.cs index 1da3a53f98..51d8d49fe6 100644 --- a/Lib9c.Policy/Policy/BlockPolicySource.cs +++ b/Lib9c.Policy/Policy/BlockPolicySource.cs @@ -72,6 +72,12 @@ public IBlockPolicy GetPolicy(Planet planet) maxTransactionsPerBlockPolicy: MaxTransactionsPerBlockPolicy.Heimdall, maxTransactionsPerSignerPerBlockPolicy: MaxTransactionsPerSignerPerBlockPolicy.Heimdall ), + Planet.HeimdallInternal => GetPolicy( + maxTransactionsBytesPolicy: MaxTransactionsBytesPolicy.Heimdall, + minTransactionsPerBlockPolicy: MinTransactionsPerBlockPolicy.Heimdall, + maxTransactionsPerBlockPolicy: MaxTransactionsPerBlockPolicy.Heimdall, + maxTransactionsPerSignerPerBlockPolicy: MaxTransactionsPerSignerPerBlockPolicy.HeimdallInternal + ), _ => throw new ArgumentException( $"Can't retrieve policy for given planet ({planet})", nameof(planet) diff --git a/Lib9c/Planet.cs b/Lib9c/Planet.cs index 4dcd7cca7b..e71c337aec 100644 --- a/Lib9c/Planet.cs +++ b/Lib9c/Planet.cs @@ -6,5 +6,6 @@ public enum Planet : byte Heimdall, Idun, OdinInternal, + HeimdallInternal, } } From 178f822ea0e0a2e1cd0fd8fa54a356b96e23952c Mon Sep 17 00:00:00 2001 From: area363 Date: Wed, 7 Feb 2024 13:31:10 +0900 Subject: [PATCH 34/64] change max transaction per signer value to default for all networks --- .../MaxTransactionsPerSignerPerBlockPolicy.cs | 25 ++++++------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/Lib9c.Policy/Policy/MaxTransactionsPerSignerPerBlockPolicy.cs b/Lib9c.Policy/Policy/MaxTransactionsPerSignerPerBlockPolicy.cs index d5f0383fa9..62fea91832 100644 --- a/Lib9c.Policy/Policy/MaxTransactionsPerSignerPerBlockPolicy.cs +++ b/Lib9c.Policy/Policy/MaxTransactionsPerSignerPerBlockPolicy.cs @@ -18,27 +18,16 @@ private MaxTransactionsPerSignerPerBlockPolicy( new MaxTransactionsPerSignerPerBlockPolicy(int.MaxValue); public static IVariableSubPolicy Odin => - Default - // Note: Introduced to prevent transactions spamming that may result in - // the chain grinding to a halt without meaningful state transitions happening. - // See https://github.com/planetarium/libplanet/issues/1449. - // Issued for v100086. - // FIXME: Starting index and value must be finalized accordingly before deployment. - .Add(new SpannedSubPolicy( - startIndex: 2_800_001, - value: 4)); + Default; + + public static IVariableSubPolicy Heimdall => + Default; // Note: For internal testing. public static IVariableSubPolicy OdinInternal => - Default - .Add(new SpannedSubPolicy( - startIndex: 2_800_001, - value: 4)); + Default; - public static IVariableSubPolicy Heimdall => - Default - .Add(new SpannedSubPolicy( - startIndex: 1, - value: 4)); + public static IVariableSubPolicy HeimdallInternal => + Default; } } From 4a2f0bead85e155064ee2201dc99ca3faeedc36b Mon Sep 17 00:00:00 2001 From: hyeon Date: Wed, 7 Feb 2024 17:33:00 +0900 Subject: [PATCH 35/64] Check skill type and ignore NormalAttack --- Lib9c/Battle/StageSimulator.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Lib9c/Battle/StageSimulator.cs b/Lib9c/Battle/StageSimulator.cs index 44a8cc3a2c..4c6c256385 100644 --- a/Lib9c/Battle/StageSimulator.cs +++ b/Lib9c/Battle/StageSimulator.cs @@ -10,6 +10,7 @@ using Nekoyume.Model.Stat; using Nekoyume.Model.State; using Nekoyume.Model.Buff; +using Nekoyume.Model.Skill; using Nekoyume.TableData; using Priority_Queue; using Skill = Nekoyume.Model.Skill.Skill; @@ -232,8 +233,17 @@ public Player Simulate() foreach (var other in Characters) { + var spdMultiplier = 0.6m; var current = Characters.GetPriority(other); - var speed = current * (other == Player && other.usedSkill != null ? 0.9m : 0.6m); + var skillRow = SkillSheet.OrderedList.FirstOrDefault(skill => + skill.Id == other.usedSkill?.SkillId); + + if (other == Player && skillRow?.SkillCategory != SkillCategory.NormalAttack) + { + spdMultiplier = 0.9m; + } + + var speed = current * spdMultiplier; Characters.UpdatePriority(other, speed); } From 0f66a3857e8a0f9c5764e84ebf01508d3c85810f Mon Sep 17 00:00:00 2001 From: hyeon Date: Wed, 7 Feb 2024 17:33:33 +0900 Subject: [PATCH 36/64] [WIP] Write test to check spd modifier effect --- .Lib9c.Tests/Model/StageSimulatorTest.cs | 95 +++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/.Lib9c.Tests/Model/StageSimulatorTest.cs b/.Lib9c.Tests/Model/StageSimulatorTest.cs index 8935de419f..42bc133777 100644 --- a/.Lib9c.Tests/Model/StageSimulatorTest.cs +++ b/.Lib9c.Tests/Model/StageSimulatorTest.cs @@ -5,6 +5,8 @@ namespace Lib9c.Tests.Model using System.Linq; using Lib9c.Tests.Action; using Libplanet.Action; + using Libplanet.Crypto; + using Nekoyume.Action; using Nekoyume.Battle; using Nekoyume.Model.BattleStatus; using Nekoyume.Model.Item; @@ -12,15 +14,18 @@ namespace Lib9c.Tests.Model using Nekoyume.Model.Stat; using Nekoyume.Model.State; using Xunit; + using Xunit.Abstractions; public class StageSimulatorTest { + private readonly ITestOutputHelper _testOutputHelper; private readonly TableSheets _tableSheets; private readonly IRandom _random; private readonly AvatarState _avatarState; - public StageSimulatorTest() + public StageSimulatorTest(ITestOutputHelper testOutputHelper) { + _testOutputHelper = testOutputHelper; _tableSheets = new TableSheets(TableSheetsImporter.ImportSheets()); _random = new TestRandom(); @@ -85,5 +90,93 @@ public void Simulate() Assert.Equal(typeof(WaveTurnEnd), filtered.Last()); Assert.Equal(1, simulator.Log.OfType().First().TurnNumber); } + + [Fact] + public void TestSkillSpeed() + { + var equipmentRow = + _tableSheets.EquipmentItemSheet.OrderedList.First(e => e.Id == 10114000); + var item = (Equipment)ItemFactory.CreateItem(equipmentRow, new TestRandom()); + Assert.Empty(item.Skills); + item.equipped = true; + _avatarState.inventory.AddItem(item); + + // Simulate with un-skilled equipment + var simulator = new StageSimulator( + _random, + _avatarState, + new List(), + null, + new List(), + 1, + 1, + _tableSheets.StageSheet[1], + _tableSheets.StageWaveSheet[1], + false, + 20, + _tableSheets.GetSimulatorSheets(), + _tableSheets.EnemySkillSheet, + _tableSheets.CostumeStatSheet, + StageSimulator.GetWaveRewards( + _random, + _tableSheets.StageSheet[1], + _tableSheets.MaterialItemSheet) + ); + var unskilledPlayer = simulator.Player; + Assert.Contains(item, unskilledPlayer.Inventory.Equipments); + simulator.Simulate(); + var unSkilledLogs = simulator.Log.Count(l => l.Character == unskilledPlayer); + + // Reset and simulate with skilled equipment + _avatarState.inventory.Equipments.First().equipped = false; + _avatarState.inventory.RemoveNonFungibleItem(item.ItemId); + Assert.Empty(_avatarState.inventory.Equipments); + var skilledItem = (Equipment)ItemFactory.CreateItem(equipmentRow, new TestRandom()); + Assert.Empty(skilledItem.Skills); + CombinationEquipment.AddSkillOption( + new AgentState(new PrivateKey().Address), + skilledItem, + new TestRandom(0), + _tableSheets.EquipmentItemSubRecipeSheetV2.OrderedList!.First(r => r.Id == 10), + _tableSheets.EquipmentItemOptionSheet, + _tableSheets.SkillSheet + ); + Assert.True(skilledItem.Skills.Any()); + skilledItem.equipped = true; + _avatarState.inventory.AddItem(skilledItem); + var random = new TestRandom(1); + simulator = new StageSimulator( + random, + _avatarState, + new List(), + null, + new List(), + 1, + 1, + _tableSheets.StageSheet[1], + _tableSheets.StageWaveSheet[1], + false, + 20, + _tableSheets.GetSimulatorSheets(), + _tableSheets.EnemySkillSheet, + _tableSheets.CostumeStatSheet, + StageSimulator.GetWaveRewards( + random, + _tableSheets.StageSheet[1], + _tableSheets.MaterialItemSheet) + ); + var skilledPlayer = simulator.Player; + Assert.Contains(skilledItem, skilledPlayer.Inventory.Equipments); + simulator.Simulate(); + var skilledActions = simulator.Log.Where(l => l.Character == skilledPlayer); + foreach (var skill in skilledActions) + { + _testOutputHelper.WriteLine(skill.ToString()); + } + + var skilledLogs = simulator.Log.Count(l => l.Character == skilledPlayer); + + Assert.True(skilledLogs > unSkilledLogs); + } } } From da49240a2347e18b4c9d9bc62cb541534fe5d274 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Thu, 8 Feb 2024 15:31:35 +0900 Subject: [PATCH 37/64] Covert GetStatsCP public for test --- Lib9c/Battle/CPHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c/Battle/CPHelper.cs b/Lib9c/Battle/CPHelper.cs index a59e48a527..e1d9d90897 100644 --- a/Lib9c/Battle/CPHelper.cs +++ b/Lib9c/Battle/CPHelper.cs @@ -123,7 +123,7 @@ public static int GetCP(INonFungibleItem tradableItem, CostumeStatSheet sheet) return 0; } - private static decimal GetStatsCP(IStats stats, int characterLevel = 1) + public static decimal GetStatsCP(IStats stats, int characterLevel = 1) { var statTuples = stats.GetStats(true); decimal cp = 0m; From 90e0c2b2dfbf733c9922bf993be375e6e57e9be5 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Thu, 8 Feb 2024 15:36:53 +0900 Subject: [PATCH 38/64] Refactor CharacterStats set stats for calculate cp --- Lib9c/Model/Character/Player.cs | 23 +++------------------- Lib9c/Model/Stat/CharacterStats.cs | 26 +++++++++++++++++++++++++ Lib9c/TableData/Rune/RuneOptionSheet.cs | 7 +++++++ 3 files changed, 36 insertions(+), 20 deletions(-) diff --git a/Lib9c/Model/Character/Player.cs b/Lib9c/Model/Character/Player.cs index ea1a52599f..1252c26b35 100644 --- a/Lib9c/Model/Character/Player.cs +++ b/Lib9c/Model/Character/Player.cs @@ -555,16 +555,7 @@ protected override void ReduceSkillCooldown() public void SetCostumeStat(CostumeStatSheet costumeStatSheet) { - var statModifiers = new List(); - foreach (var itemId in costumes.Select(costume => costume.Id)) - { - statModifiers.AddRange( - costumeStatSheet.OrderedList - .Where(r => r.CostumeId == itemId) - .Select(row => new StatModifier(row.StatType, StatModifier.OperationType.Add, (int) row.Stat)) - ); - } - Stats.SetCostume(statModifiers); + Stats.SetCostumeStat(costumes, costumeStatSheet); ResetCurrentHP(); } @@ -575,20 +566,12 @@ public void SetRune( { foreach (var rune in runes) { - if (!runeOptionSheet.TryGetValue(rune.RuneId, out var optionRow) || - !optionRow.LevelOptionMap.TryGetValue(rune.Level, out var optionInfo)) + if (!runeOptionSheet.TryGetOptionInfo(rune.RuneId, rune.Level, out var optionInfo)) { continue; } - var statModifiers = new List(); - statModifiers.AddRange( - optionInfo.Stats.Select(x => - new StatModifier( - x.stat.StatType, - x.operationType, - x.stat.BaseValueAsLong))); - Stats.AddRune(statModifiers); + Stats.AddRuneStat(optionInfo); ResetCurrentHP(); if (optionInfo.SkillId == default || diff --git a/Lib9c/Model/Stat/CharacterStats.cs b/Lib9c/Model/Stat/CharacterStats.cs index 7f631fcdfb..ed610b0f54 100644 --- a/Lib9c/Model/Stat/CharacterStats.cs +++ b/Lib9c/Model/Stat/CharacterStats.cs @@ -533,5 +533,31 @@ public override object Clone() } } } + + public void SetCostumeStat(IReadOnlyCollection costumes, CostumeStatSheet costumeStatSheet) + { + var statModifiers = new List(); + foreach (var itemId in costumes.Select(costume => costume.Id)) + { + statModifiers.AddRange( + costumeStatSheet.OrderedList + .Where(r => r.CostumeId == itemId) + .Select(row => new StatModifier(row.StatType, StatModifier.OperationType.Add, (int) row.Stat)) + ); + } + SetCostume(statModifiers); + } + + public void AddRuneStat(RuneOptionSheet.Row.RuneOptionInfo optionInfo) + { + var statModifiers = new List(); + statModifiers.AddRange( + optionInfo.Stats.Select(x => + new StatModifier( + x.stat.StatType, + x.operationType, + x.stat.BaseValueAsLong))); + AddRune(statModifiers); + } } } diff --git a/Lib9c/TableData/Rune/RuneOptionSheet.cs b/Lib9c/TableData/Rune/RuneOptionSheet.cs index 711892e5c1..02634a31e8 100644 --- a/Lib9c/TableData/Rune/RuneOptionSheet.cs +++ b/Lib9c/TableData/Rune/RuneOptionSheet.cs @@ -154,5 +154,12 @@ protected override void AddRow(int key, Row value) var pair = value.LevelOptionMap.OrderBy(x => x.Key).First(); row.LevelOptionMap[pair.Key] = pair.Value; } + + public bool TryGetOptionInfo(int runId, int level, out Row.RuneOptionInfo optionInfo) + { + optionInfo = null; + return TryGetValue(runId, out var row) && + row.LevelOptionMap.TryGetValue(level, out optionInfo); + } } } From 146b3fe5968e914ad374e8ae9fef222b79036457 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Thu, 8 Feb 2024 15:39:16 +0900 Subject: [PATCH 39/64] Calculate collection stats cp --- .Lib9c.Tests/Battle/CPHelperTest.cs | 83 +++++++++++++++++++++++++++++ Lib9c/Action/HackAndSlashSweep.cs | 55 ++++++++++++------- Lib9c/Action/Raid.cs | 2 +- Lib9c/Battle/CPHelper.cs | 38 ++++++++++--- 4 files changed, 152 insertions(+), 26 deletions(-) create mode 100644 .Lib9c.Tests/Battle/CPHelperTest.cs diff --git a/.Lib9c.Tests/Battle/CPHelperTest.cs b/.Lib9c.Tests/Battle/CPHelperTest.cs new file mode 100644 index 0000000000..dc49889b41 --- /dev/null +++ b/.Lib9c.Tests/Battle/CPHelperTest.cs @@ -0,0 +1,83 @@ +namespace Lib9c.Tests +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Lib9c.Tests.Action; + using Libplanet.Action; + using Nekoyume; + using Nekoyume.Battle; + using Nekoyume.Model.Item; + using Nekoyume.Model.Skill; + using Nekoyume.Model.Stat; + using Nekoyume.TableData; + using Xunit; + + public class CPHelperTest + { + private readonly TableSheets _tableSheets; + + public CPHelperTest() + { + _tableSheets = new TableSheets(TableSheetsImporter.ImportSheets()); + } + + [Theory] + [InlineData(1)] + [InlineData(10)] + public void GetStatsCp(int level) + { + CharacterSheet.Row row = + _tableSheets.CharacterSheet[GameConfig.DefaultAvatarCharacterId]; + var characterStats = new CharacterStats(row, level); + Assert.Equal(CPHelper.GetStatsCP(row.ToStats(level), level), CPHelper.GetStatsCP(characterStats, level)); + } + + [Fact] + public void TotalCP() + { + CharacterSheet.Row row = + _tableSheets.CharacterSheet[GameConfig.DefaultAvatarCharacterId]; + CostumeStatSheet costumeStatSheet = _tableSheets.CostumeStatSheet; + var random = new TestRandom(); + // Arrange + List equipments = new List(); // Populate your equipments + var equipment = (Equipment)ItemFactory.CreateItem(_tableSheets.EquipmentItemSheet.Values.First(), random); + var skill = SkillFactory.Get(_tableSheets.SkillSheet.Values.First(), 0, 0, 0, StatType.HP); + equipment.Skills.Add(skill); + equipments.Add(equipment); + List costumes = new List(); // Populate your costumes + var costume = ItemFactory.CreateCostume(_tableSheets.CostumeItemSheet.Values.First(), random.GenerateRandomGuid()); + costumes.Add(costume); + List runeOptions = new List(); // Populate your runeOptions + var runeOptionInfo = _tableSheets.RuneOptionSheet.Values.First().LevelOptionMap[1]; + runeOptions.Add(runeOptionInfo); + int level = 1; // A level for testing + List collectionStatModifiers = new List(); // Populate your collectionStatModifiers + var result = CPHelper.TotalCP(equipments, costumes, runeOptions, level, row, costumeStatSheet, collectionStatModifiers); + var characterStats = new CharacterStats(row, level); + characterStats.SetEquipments(equipments, new EquipmentItemSetEffectSheet()); + characterStats.SetCostumeStat(costumes, costumeStatSheet); + characterStats.AddRuneStat(runeOptionInfo); + foreach (StatType type in Enum.GetValues(typeof(StatType))) + { + if (type == StatType.NONE) + { + continue; + } + + collectionStatModifiers.Add(new StatModifier(type, StatModifier.OperationType.Percentage, 100)); + } + + characterStats.SetCollections(collectionStatModifiers); + var ccp = 0m; + foreach (var (statType, value) in characterStats.CollectionStats.GetStats()) + { + ccp += CPHelper.GetStatCP(statType, value); + } + + var collectionCp = CPHelper.DecimalToInt(ccp); + Assert.Equal(result + collectionCp, CPHelper.TotalCP(equipments, costumes, runeOptions, level, row, costumeStatSheet, collectionStatModifiers)); + } + } +} diff --git a/Lib9c/Action/HackAndSlashSweep.cs b/Lib9c/Action/HackAndSlashSweep.cs index b5409a377f..b683b6109a 100644 --- a/Lib9c/Action/HackAndSlashSweep.cs +++ b/Lib9c/Action/HackAndSlashSweep.cs @@ -12,6 +12,7 @@ using Nekoyume.Helper; using Nekoyume.Model.EnumType; using Nekoyume.Model.Item; +using Nekoyume.Model.Stat; using Nekoyume.Model.State; using Nekoyume.Module; using Nekoyume.TableData; @@ -100,25 +101,31 @@ public override IWorld Execute(IActionContext context) $"{addressesHex}Aborted as the avatar state of the signer was failed to load."); } + var collectionExist = states.TryGetCollectionState(avatarAddress, out var collectionState) && collectionState.Ids.Any(); + var sheetTypes = new List + { + typeof(WorldSheet), + typeof(StageSheet), + typeof(MaterialItemSheet), + typeof(StageWaveSheet), + typeof(CharacterLevelSheet), + typeof(ItemRequirementSheet), + typeof(EquipmentItemRecipeSheet), + typeof(EquipmentItemSubRecipeSheetV2), + typeof(EquipmentItemOptionSheet), + typeof(CharacterSheet), + typeof(CostumeStatSheet), + typeof(SweepRequiredCPSheet), + typeof(StakeActionPointCoefficientSheet), + typeof(RuneListSheet), + typeof(RuneOptionSheet), + }; + if (collectionExist) + { + sheetTypes.Add(typeof(CollectionSheet)); + } var sheets = states.GetSheets( - sheetTypes: new[] - { - typeof(WorldSheet), - typeof(StageSheet), - typeof(MaterialItemSheet), - typeof(StageWaveSheet), - typeof(CharacterLevelSheet), - typeof(ItemRequirementSheet), - typeof(EquipmentItemRecipeSheet), - typeof(EquipmentItemSubRecipeSheetV2), - typeof(EquipmentItemOptionSheet), - typeof(CharacterSheet), - typeof(CostumeStatSheet), - typeof(SweepRequiredCPSheet), - typeof(StakeActionPointCoefficientSheet), - typeof(RuneListSheet), - typeof(RuneOptionSheet), - }); + sheetTypes: sheetTypes); var worldSheet = sheets.GetSheet(); if (!worldSheet.TryGetValue(worldId, out var worldRow, false)) @@ -245,11 +252,21 @@ public override IWorld Execute(IActionContext context) throw new SheetRowNotFoundException("CharacterSheet", avatarState.characterId); } + var collectionModifiers = new List(); + if (collectionExist) + { + var collectionSheet = sheets.GetSheet(); + foreach (var collectionId in collectionState.Ids) + { + collectionModifiers.AddRange(collectionSheet[collectionId].StatModifiers); + } + } + var costumeStatSheet = sheets.GetSheet(); var cp = CPHelper.TotalCP( equipmentList, costumeList, runeOptions, avatarState.level, - characterRow, costumeStatSheet); + characterRow, costumeStatSheet, collectionModifiers); if (cp < cpRow.RequiredCP) { throw new NotEnoughCombatPointException( diff --git a/Lib9c/Action/Raid.cs b/Lib9c/Action/Raid.cs index 7cba0dfea8..4fc23652c0 100644 --- a/Lib9c/Action/Raid.cs +++ b/Lib9c/Action/Raid.cs @@ -273,7 +273,7 @@ public override IWorld Execute(IActionContext context) var cp = CPHelper.TotalCP( equipmentList, costumeList, runeOptions, avatarState.level, - characterRow, costumeStatSheet); + characterRow, costumeStatSheet, collectionModifiers); long score = simulator.DamageDealt; raiderState.Update(avatarState, cp, score, PayNcg, context.BlockIndex); diff --git a/Lib9c/Battle/CPHelper.cs b/Lib9c/Battle/CPHelper.cs index e1d9d90897..fa4668280e 100644 --- a/Lib9c/Battle/CPHelper.cs +++ b/Lib9c/Battle/CPHelper.cs @@ -12,14 +12,39 @@ namespace Nekoyume.Battle public static class CPHelper { public static int TotalCP( - IEnumerable equipments, - IEnumerable costumes, - IEnumerable runeOptions, + IReadOnlyCollection equipments, + IReadOnlyCollection costumes, + IReadOnlyCollection runeOptions, int level, CharacterSheet.Row row, - CostumeStatSheet costumeStatSheet) + CostumeStatSheet costumeStatSheet, List collectionStatModifiers) { - var levelStatsCp = GetStatsCP(row.ToStats(level), level); + decimal levelStatsCp; + var collectionCp = 0m; + // CharacterStats.BaseStats CP equals row.ToStats + if (collectionStatModifiers.Any()) + { + // Prepare CharacterStats for calculate collection Stats + var characterStats = new CharacterStats(row, level); + characterStats.SetEquipments(equipments, new EquipmentItemSetEffectSheet()); + characterStats.SetCostumeStat(costumes, costumeStatSheet); + foreach (var runeOption in runeOptions) + { + characterStats.AddRuneStat(runeOption); + } + + characterStats.SetCollections(collectionStatModifiers); + levelStatsCp = GetStatsCP(characterStats.BaseStats, level); + foreach (var (statType, value) in characterStats.CollectionStats.GetStats()) + { + collectionCp += GetStatCP(statType, value); + } + } + else + { + levelStatsCp = GetStatsCP(row.ToStats(level), level); + } + var equipmentsCp = 0; var costumeCp = 0; var runeCp = 0; @@ -36,7 +61,8 @@ public static int TotalCP( { runeCp += runeOption.Cp; } - var totalCp = DecimalToInt(levelStatsCp + equipmentsCp + costumeCp + runeCp); + + var totalCp = DecimalToInt(levelStatsCp + equipmentsCp + costumeCp + runeCp + collectionCp); return totalCp; } From 01d10d30ec99f22b1ee98abaacaeb94fe9ddd8aa Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Thu, 8 Feb 2024 16:34:26 +0900 Subject: [PATCH 40/64] Fix Typo Co-authored-by: Kim sm. --- Lib9c/TableData/Rune/RuneOptionSheet.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib9c/TableData/Rune/RuneOptionSheet.cs b/Lib9c/TableData/Rune/RuneOptionSheet.cs index 02634a31e8..b325b7d02c 100644 --- a/Lib9c/TableData/Rune/RuneOptionSheet.cs +++ b/Lib9c/TableData/Rune/RuneOptionSheet.cs @@ -155,10 +155,10 @@ protected override void AddRow(int key, Row value) row.LevelOptionMap[pair.Key] = pair.Value; } - public bool TryGetOptionInfo(int runId, int level, out Row.RuneOptionInfo optionInfo) + public bool TryGetOptionInfo(int runeId, int level, out Row.RuneOptionInfo optionInfo) { optionInfo = null; - return TryGetValue(runId, out var row) && + return TryGetValue(runeId, out var row) && row.LevelOptionMap.TryGetValue(level, out optionInfo); } } From 9fc285ad3a5a307de5d057acc64e91fc92b49ce0 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Thu, 8 Feb 2024 16:36:14 +0900 Subject: [PATCH 41/64] Update Lib9c/Battle/CPHelper.cs Co-authored-by: Jonny <138189475+jonny-jeahyunchoi@users.noreply.github.com> --- Lib9c/Battle/CPHelper.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Lib9c/Battle/CPHelper.cs b/Lib9c/Battle/CPHelper.cs index fa4668280e..4e560135a8 100644 --- a/Lib9c/Battle/CPHelper.cs +++ b/Lib9c/Battle/CPHelper.cs @@ -19,7 +19,7 @@ public static int TotalCP( CharacterSheet.Row row, CostumeStatSheet costumeStatSheet, List collectionStatModifiers) { - decimal levelStatsCp; + decimal levelStatsCp = GetStatsCP(row.ToStats(level), level); var collectionCp = 0m; // CharacterStats.BaseStats CP equals row.ToStats if (collectionStatModifiers.Any()) @@ -34,16 +34,11 @@ public static int TotalCP( } characterStats.SetCollections(collectionStatModifiers); - levelStatsCp = GetStatsCP(characterStats.BaseStats, level); foreach (var (statType, value) in characterStats.CollectionStats.GetStats()) { collectionCp += GetStatCP(statType, value); } } - else - { - levelStatsCp = GetStatsCP(row.ToStats(level), level); - } var equipmentsCp = 0; var costumeCp = 0; From 6c0d2b52d45cf73e748e68f4665019f6fe352855 Mon Sep 17 00:00:00 2001 From: hyeon Date: Thu, 8 Feb 2024 18:09:37 +0900 Subject: [PATCH 42/64] Test with stage 3 to get more action logs --- .Lib9c.Tests/Model/StageSimulatorTest.cs | 25 ++++++++++++------------ 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/.Lib9c.Tests/Model/StageSimulatorTest.cs b/.Lib9c.Tests/Model/StageSimulatorTest.cs index 42bc133777..8c472c7afb 100644 --- a/.Lib9c.Tests/Model/StageSimulatorTest.cs +++ b/.Lib9c.Tests/Model/StageSimulatorTest.cs @@ -103,23 +103,23 @@ public void TestSkillSpeed() // Simulate with un-skilled equipment var simulator = new StageSimulator( - _random, + new TestRandom(1), _avatarState, new List(), null, new List(), 1, - 1, - _tableSheets.StageSheet[1], - _tableSheets.StageWaveSheet[1], + 3, + _tableSheets.StageSheet[3], + _tableSheets.StageWaveSheet[7], false, 20, _tableSheets.GetSimulatorSheets(), _tableSheets.EnemySkillSheet, _tableSheets.CostumeStatSheet, StageSimulator.GetWaveRewards( - _random, - _tableSheets.StageSheet[1], + new TestRandom(1), + _tableSheets.StageSheet[3], _tableSheets.MaterialItemSheet) ); var unskilledPlayer = simulator.Player; @@ -144,25 +144,24 @@ public void TestSkillSpeed() Assert.True(skilledItem.Skills.Any()); skilledItem.equipped = true; _avatarState.inventory.AddItem(skilledItem); - var random = new TestRandom(1); simulator = new StageSimulator( - random, + new TestRandom(1), _avatarState, new List(), null, new List(), 1, - 1, - _tableSheets.StageSheet[1], - _tableSheets.StageWaveSheet[1], + 3, + _tableSheets.StageSheet[3], + _tableSheets.StageWaveSheet[7], false, 20, _tableSheets.GetSimulatorSheets(), _tableSheets.EnemySkillSheet, _tableSheets.CostumeStatSheet, StageSimulator.GetWaveRewards( - random, - _tableSheets.StageSheet[1], + new TestRandom(1), + _tableSheets.StageSheet[3], _tableSheets.MaterialItemSheet) ); var skilledPlayer = simulator.Player; From 70e686b4aba40a92b940f0f1743b5da85863fbad Mon Sep 17 00:00:00 2001 From: hyeon Date: Thu, 8 Feb 2024 18:10:01 +0900 Subject: [PATCH 43/64] Check battle log with character ID --- .Lib9c.Tests/Model/StageSimulatorTest.cs | 29 ++++++++++++++++-------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/.Lib9c.Tests/Model/StageSimulatorTest.cs b/.Lib9c.Tests/Model/StageSimulatorTest.cs index 8c472c7afb..a3ba50f62e 100644 --- a/.Lib9c.Tests/Model/StageSimulatorTest.cs +++ b/.Lib9c.Tests/Model/StageSimulatorTest.cs @@ -125,7 +125,14 @@ public void TestSkillSpeed() var unskilledPlayer = simulator.Player; Assert.Contains(item, unskilledPlayer.Inventory.Equipments); simulator.Simulate(); - var unSkilledLogs = simulator.Log.Count(l => l.Character == unskilledPlayer); + + var unSkilledActions = simulator.Log.Where(l => l.Character?.Id == unskilledPlayer.Id); + /* foreach (var log in unSkilledActions) + * { + * _testOutputHelper.WriteLine($"{log}"); + * } + * _testOutputHelper.WriteLine("============================================="); + */ // Reset and simulate with skilled equipment _avatarState.inventory.Equipments.First().equipped = false; @@ -133,6 +140,8 @@ public void TestSkillSpeed() Assert.Empty(_avatarState.inventory.Equipments); var skilledItem = (Equipment)ItemFactory.CreateItem(equipmentRow, new TestRandom()); Assert.Empty(skilledItem.Skills); + + // Add BlowAttack CombinationEquipment.AddSkillOption( new AgentState(new PrivateKey().Address), skilledItem, @@ -167,15 +176,17 @@ public void TestSkillSpeed() var skilledPlayer = simulator.Player; Assert.Contains(skilledItem, skilledPlayer.Inventory.Equipments); simulator.Simulate(); - var skilledActions = simulator.Log.Where(l => l.Character == skilledPlayer); - foreach (var skill in skilledActions) - { - _testOutputHelper.WriteLine(skill.ToString()); - } - - var skilledLogs = simulator.Log.Count(l => l.Character == skilledPlayer); + var skilledActions = simulator.Log.Where(l => l.Character?.Id == skilledPlayer.Id); + /* + * foreach (var log in skilledActions) + * { + * _testOutputHelper.WriteLine($"{log}"); + * } + */ - Assert.True(skilledLogs > unSkilledLogs); + Assert.Contains(skilledActions, e => e is BlowAttack); + // Skill scales speed by 0.9, so this makes way more player actions. + Assert.True(skilledActions.Count() > unSkilledActions.Count()); } } } From 8f1b45494ca82c8b6550749e92d08b4b98f7b2c9 Mon Sep 17 00:00:00 2001 From: moreal Date: Thu, 25 Jan 2024 09:09:49 +0900 Subject: [PATCH 44/64] Bump VersionPrefix to 1.8.0 --- Lib9c/Lib9c.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c/Lib9c.csproj b/Lib9c/Lib9c.csproj index 5968cbbfc8..c21c85d8ac 100644 --- a/Lib9c/Lib9c.csproj +++ b/Lib9c/Lib9c.csproj @@ -9,7 +9,7 @@ .obj Nekoyume 9 - 1.1.0 + 1.8.0 true Debug;Release AnyCPU From 8a65e5ae0143a88a2aa7ae5cb98b35c447655d68 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Tue, 13 Feb 2024 20:37:03 +0900 Subject: [PATCH 45/64] Apply missing collection modifier --- .../Action/Scenario/CollectionScenarioTest.cs | 50 +++++++++++++++++++ Lib9c/Action/EventDungeonBattle.cs | 41 ++++++++++----- 2 files changed, 79 insertions(+), 12 deletions(-) diff --git a/.Lib9c.Tests/Action/Scenario/CollectionScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/CollectionScenarioTest.cs index 74c4e7d4fd..720f58cc4a 100644 --- a/.Lib9c.Tests/Action/Scenario/CollectionScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/CollectionScenarioTest.cs @@ -8,6 +8,7 @@ namespace Lib9c.Tests.Action.Scenario using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; + using Nekoyume.Extensions; using Nekoyume.Model.EnumType; using Nekoyume.Model.State; using Nekoyume.Module; @@ -226,5 +227,54 @@ public void BattleArena(bool collectionExist) }); } } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void EventDungeonBattle(bool collectionExist) + { + var states = _initialState; + if (collectionExist) + { + var collectionState = new CollectionState(); + collectionState.Ids.Add(1); + states = states.SetCollectionState(_avatarAddress, collectionState); + } + + foreach (var (key, value) in _sheets) + { + if (key == nameof(CollectionSheet) && !collectionExist) + { + continue; + } + + states = states + .SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize()); + } + + var scheduleRow = _tableSheets.EventScheduleSheet.Values.First(); + Assert.True(_tableSheets.EventDungeonSheet.TryGetRowByEventScheduleId( + scheduleRow.Id, + out var eventDungeonRow)); + var action = new EventDungeonBattle + { + AvatarAddress = _avatarAddress, + Equipments = new List(), + Costumes = new List(), + RuneInfos = new List(), + Foods = new List(), + EventScheduleId = scheduleRow.Id, + EventDungeonStageId = eventDungeonRow.StageBegin, + EventDungeonId = eventDungeonRow.Id, + }; + + action.Execute(new ActionContext + { + PreviousState = states, + Signer = _agentAddress, + RandomSeed = 0, + BlockIndex = scheduleRow.StartBlockIndex, + }); + } } } diff --git a/Lib9c/Action/EventDungeonBattle.cs b/Lib9c/Action/EventDungeonBattle.cs index 2fe1f51b1a..675995193c 100644 --- a/Lib9c/Action/EventDungeonBattle.cs +++ b/Lib9c/Action/EventDungeonBattle.cs @@ -150,22 +150,29 @@ public override IWorld Execute(IActionContext context) sw.Elapsed); // ~Get AvatarState + var collectionExist = states.TryGetCollectionState(AvatarAddress, out var collectionState); // Get sheets sw.Restart(); + var sheetTypes = new List + { + typeof(EventScheduleSheet), + typeof(EventDungeonSheet), + typeof(EventDungeonStageSheet), + typeof(EventDungeonStageWaveSheet), + typeof(EnemySkillSheet), + typeof(CostumeStatSheet), + typeof(MaterialItemSheet), + typeof(RuneListSheet), + }; + if (collectionExist) + { + sheetTypes.Add(typeof(CollectionSheet)); + } var sheets = states.GetSheets( containSimulatorSheets: true, containValidateItemRequirementSheets: true, - sheetTypes: new[] - { - typeof(EventScheduleSheet), - typeof(EventDungeonSheet), - typeof(EventDungeonStageSheet), - typeof(EventDungeonStageWaveSheet), - typeof(EnemySkillSheet), - typeof(CostumeStatSheet), - typeof(MaterialItemSheet), - typeof(RuneListSheet), - }); + sheetTypes: sheetTypes +); sw.Stop(); Log.Verbose( "[{ActionTypeString}][{AddressesHex}] Get sheets: {Elapsed}", @@ -329,6 +336,16 @@ is Bencodex.Types.List serializedEventDungeonInfoList } var random = context.GetRandom(); + var collectionModifiers = new List(); + if (collectionExist) + { + var collectionSheet = sheets.GetSheet(); + foreach (var collectionId in collectionState.Ids) + { + collectionModifiers.AddRange(collectionSheet[collectionId].StatModifiers); + } + } + var simulator = new StageSimulator( random, avatarState, @@ -349,7 +366,7 @@ is Bencodex.Types.List serializedEventDungeonInfoList stageRow, sheets.GetSheet(), PlayCount), - new List()); + collectionModifiers); simulator.Simulate(); sw.Stop(); Log.Verbose( From f6203fb2438f89f863a0e74a191c663d49f1ee76 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Tue, 13 Feb 2024 23:19:25 +0900 Subject: [PATCH 46/64] Rename StatWithItems to StatWithoutBuffs --- Lib9c/Model/Buff/BuffFactory.cs | 2 +- Lib9c/Model/Stat/CharacterStats.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib9c/Model/Buff/BuffFactory.cs b/Lib9c/Model/Buff/BuffFactory.cs index 4435e32298..331714c18e 100644 --- a/Lib9c/Model/Buff/BuffFactory.cs +++ b/Lib9c/Model/Buff/BuffFactory.cs @@ -81,7 +81,7 @@ public static IList GetBuffs( var additionalValue = skill.Power; if (skill.ReferencedStatType != StatType.NONE) { - var statMap = stats.StatWithItems; + var statMap = stats.StatWithoutBuffs; var multiplier = skill.StatPowerRatio / 10000m; additionalValue += (int)Math.Round(statMap.GetStat(skill.ReferencedStatType) * multiplier); } diff --git a/Lib9c/Model/Stat/CharacterStats.cs b/Lib9c/Model/Stat/CharacterStats.cs index ed610b0f54..783275065c 100644 --- a/Lib9c/Model/Stat/CharacterStats.cs +++ b/Lib9c/Model/Stat/CharacterStats.cs @@ -35,7 +35,7 @@ public class CharacterStats : Stats, IBaseAndAdditionalStats, ICloneable private readonly List _collectionStatModifiers = new List(); private readonly Dictionary _buffStatModifiers = new Dictionary(); - public readonly StatMap StatWithItems = new StatMap(); + public readonly StatMap StatWithoutBuffs = new StatMap(); public int Level { get; private set; } @@ -453,8 +453,8 @@ private void UpdateCostumeStats() private void UpdateCollectionStats() { _collectionStats.Set(_collectionStatModifiers, _baseStats, _equipmentStats, _consumableStats, _runeStats, _costumeStats); - Set(StatWithItems, _baseStats, _equipmentStats, _consumableStats, _runeStats, _costumeStats, _collectionStats); - foreach (var stat in StatWithItems.GetDecimalStats(false)) + Set(StatWithoutBuffs, _baseStats, _equipmentStats, _consumableStats, _runeStats, _costumeStats, _collectionStats); + foreach (var stat in StatWithoutBuffs.GetDecimalStats(false)) { if (!LegacyDecimalStatTypes.Contains(stat.StatType)) { From c80c5569fa15c3a37fb60be936c225cc67846d85 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Tue, 13 Feb 2024 23:20:30 +0900 Subject: [PATCH 47/64] Fix IncreaseHPForArena --- .Lib9c.Tests/Model/ArenaSimulatorTest.cs | 48 ++++++++++ .Lib9c.Tests/Model/PlayerTest.cs | 100 ++++++++++++++++++++ .Lib9c.Tests/Model/Skill/BuffFactoryTest.cs | 60 ++++++++++++ Lib9c/Model/Stat/CharacterStats.cs | 22 ++++- 4 files changed, 228 insertions(+), 2 deletions(-) diff --git a/.Lib9c.Tests/Model/ArenaSimulatorTest.cs b/.Lib9c.Tests/Model/ArenaSimulatorTest.cs index 2e2f65118d..c4560d7bb5 100644 --- a/.Lib9c.Tests/Model/ArenaSimulatorTest.cs +++ b/.Lib9c.Tests/Model/ArenaSimulatorTest.cs @@ -1,5 +1,6 @@ namespace Lib9c.Tests { + using System; using System.Collections.Generic; using System.Linq; using Lib9c.Tests.Action; @@ -7,6 +8,8 @@ namespace Lib9c.Tests using Nekoyume.Arena; using Nekoyume.Model; using Nekoyume.Model.BattleStatus.Arena; + using Nekoyume.Model.Item; + using Nekoyume.Model.Skill; using Nekoyume.Model.Stat; using Nekoyume.Model.State; using Xunit; @@ -135,5 +138,50 @@ public void HpIncreasingModifier(int? modifier) Assert.Equal(player.Stats.BaseHP * expectedHpModifier, player.CurrentHP); } } + + [Fact] + public void Thorns() + { + var random = new TestRandom(); + var equipmentRow = + _tableSheets.EquipmentItemSheet.Values.First(r => r.Stat.StatType == StatType.HP); + var skillId = 270000; + var skillRow = _tableSheets.SkillSheet[skillId]; + var skill = SkillFactory.Get(skillRow, 0, 100, 700, StatType.HP); + var equipment = (Equipment)ItemFactory.CreateItem(equipmentRow, random); + equipment.Skills.Add(skill); + var equipmentRow2 = + _tableSheets.EquipmentItemSheet.Values.First(r => r.Stat.StatType == StatType.HIT); + var equipment2 = (Equipment)ItemFactory.CreateItem(equipmentRow2, random); + equipment2.Skills.Add(skill); + var avatarState1 = _avatarState1; + var avatarState2 = _avatarState2; + avatarState1.inventory.AddItem(equipment); + avatarState2.inventory.AddItem(equipment2); + + var arenaAvatarState1 = new ArenaAvatarState(avatarState1); + arenaAvatarState1.UpdateEquipment(new List + { + equipment.ItemId, + }); + var arenaAvatarState2 = new ArenaAvatarState(avatarState2); + arenaAvatarState2.UpdateEquipment(new List + { + equipment2.ItemId, + }); + + var simulator = new ArenaSimulator(_random); + var myDigest = new ArenaPlayerDigest(avatarState1, arenaAvatarState1); + var enemyDigest = new ArenaPlayerDigest(avatarState2, arenaAvatarState2); + var arenaSheets = _tableSheets.GetArenaSimulatorSheets(); + var log = simulator.Simulate(myDigest, enemyDigest, arenaSheets, new List(), new List(), true); + var ticks = log.Events + .OfType() + .ToList(); + var challengerTick = ticks.First(r => !r.Character.IsEnemy); + var enemyTick = ticks.First(r => r.Character.IsEnemy); + Assert.True(challengerTick.Character.HP > enemyTick.Character.HP); + Assert.True(enemyTick.SkillInfos.First().Effect > challengerTick.SkillInfos.First().Effect); + } } } diff --git a/.Lib9c.Tests/Model/PlayerTest.cs b/.Lib9c.Tests/Model/PlayerTest.cs index 65000f14c0..1a609bb465 100644 --- a/.Lib9c.Tests/Model/PlayerTest.cs +++ b/.Lib9c.Tests/Model/PlayerTest.cs @@ -813,5 +813,105 @@ public void StatsLayerTest() // 20 + 1 + 18 + 1829 + 235 + 100 + 1662 // Assert.Equal(3865, player.ATK); } + + [Fact] + public void IncreaseHpForArena() + { + var row = _tableSheets.EquipmentItemSheet.Values.First(r => + r.Stat.StatType == StatType.HP); + var equipment = (Equipment)ItemFactory.CreateItem(_tableSheets.ItemSheet[row.Id], new TestRandom()); + equipment.equipped = true; + _avatarState.inventory.AddItem(equipment); + var costumeStatRow = + _tableSheets.CostumeStatSheet.Values.First(r => r.StatType == StatType.HP); + var costumeId = costumeStatRow.CostumeId; + var costume = ItemFactory.CreateCostume(_tableSheets.CostumeItemSheet[costumeId], Guid.NewGuid()); + costume.equipped = true; + _avatarState.inventory.AddItem(costume); + var foodRow = + _tableSheets.ConsumableItemSheet.Values.First(r => + r.Stats.Any(s => s.StatType == StatType.HP)); + var food = (Consumable)ItemFactory.CreateItem(foodRow, _random); + _avatarState.inventory.AddItem(food); + + // Update equipment stats + var player = new Player( + _avatarState, + _tableSheets.CharacterSheet, + _tableSheets.CharacterLevelSheet, + _tableSheets.EquipmentItemSetEffectSheet + ); + Assert.Equal(player.Stats.BaseHP + player.Stats.EquipmentStats.HP, player.HP); + // BaseHp 300, EquipmentStats 30 + // Assert.Equal(330, player.HP); + var equipmentLayerHp = player.HP; + + // Update consumable stats + player.Use(new List + { + food.ItemId, + }); + Assert.Equal(equipmentLayerHp + food.Stats.Where(s => s.StatType == StatType.HP).Sum(s => s.BaseValueAsLong), player.HP); + // ConsumableStats 29 + // Assert.Equal(359, player.HP); + var consumableLayerHp = player.HP; + + // Update rune stat + var runeId = 30001; + var runeState = new RuneState(runeId); + runeState.LevelUp(); + Assert.Equal(1, runeState.Level); + + var runeStates = new List + { + runeState, + }; + player.SetRune(runeStates, _tableSheets.RuneOptionSheet, _tableSheets.SkillSheet); + var runeOptionRow = _tableSheets.RuneOptionSheet.Values.First(r => r.RuneId == runeId); + var runeHp = runeOptionRow.LevelOptionMap[1].Stats.Sum(r => r.stat.BaseValueAsLong); + Assert.Equal(consumableLayerHp + runeHp, player.HP); + // RuneStats 520 + // Assert.Equal(879, player.HP); + var runeLayerHp = player.HP; + + // Update costume stats + player.SetCostumeStat(_tableSheets.CostumeStatSheet); + Assert.Equal(runeLayerHp + costumeStatRow.Stat, player.HP); + // CostumeStats 26990 + // Assert.Equal(27869, player.HP); + var costumeLayerHp = player.HP; + + // Update collection stat + var modifiers = new List(); + var addModifier = new StatModifier(StatType.HP, StatModifier.OperationType.Add, 100); + modifiers.Add(new StatModifier(StatType.HP, StatModifier.OperationType.Percentage, 200)); + modifiers.Add(addModifier); + modifiers.Add(new StatModifier(StatType.HP, StatModifier.OperationType.Percentage, -100)); + player.Stats.SetCollections(modifiers); + Assert.Equal(costumeLayerHp + addModifier.Value + costumeLayerHp, player.HP); + // CollectionStats 100 + 27869(100%) + // Assert.Equal(55838, player.HP); + var collectionLayerHp = player.HP; + + // Arena + player.Stats.IsArenaCharacter = true; + player.Stats.IncreaseHpForArena(); + Assert.Equal(collectionLayerHp * 2, player.HP); + Assert.Equal(player.HP, player.Stats.StatWithoutBuffs.HP); + // Assert.Equal(111676, player.HP); + var arenaHp = player.HP; + + var statBuffs = new List(); + var percentageBuffRow = _tableSheets.StatBuffSheet.Values.First(r => + r.StatType == StatType.HP && + r.OperationType == StatModifier.OperationType.Percentage); + var percentageBuff = new StatBuff(percentageBuffRow); + statBuffs.Add(percentageBuff); + var percentageModifier = percentageBuff.GetModifier(); + var percentageBuffAtk = (long)percentageModifier.GetModifiedValue(arenaHp); + player.Stats.SetBuffs(statBuffs); + Assert.Equal(arenaHp + percentageBuffAtk, player.HP); + Assert.Equal(arenaHp, player.Stats.StatWithoutBuffs.HP); + } } } diff --git a/.Lib9c.Tests/Model/Skill/BuffFactoryTest.cs b/.Lib9c.Tests/Model/Skill/BuffFactoryTest.cs index 59ad85b541..2be42919c0 100644 --- a/.Lib9c.Tests/Model/Skill/BuffFactoryTest.cs +++ b/.Lib9c.Tests/Model/Skill/BuffFactoryTest.cs @@ -1,6 +1,7 @@ namespace Lib9c.Tests.Model.Skill { using System.Collections.Generic; + using System.Linq; using Lib9c.Tests.Action; using Nekoyume.Arena; using Nekoyume.Model; @@ -88,5 +89,64 @@ public void GetBuffs() var buff = Assert.IsType(Assert.Single(buffs)); Assert.NotNull(buff.CustomField); } + + [Fact] + public void Thorns() + { + var player = new Player( + level: 1, + _tableSheets.CharacterSheet, + _tableSheets.CharacterLevelSheet, + _tableSheets.EquipmentItemSetEffectSheet); + // Aegis aura atk down + var skillId = 270000; + var skillRow = _tableSheets.SkillSheet[skillId]; + var skill = SkillFactory.Get(skillRow, 0, 100, 700, StatType.HP); + var buffs = BuffFactory.GetBuffs( + player.Stats, + skill, + _tableSheets.SkillBuffSheet, + _tableSheets.StatBuffSheet, + _tableSheets.SkillActionBuffSheet, + _tableSheets.ActionBuffSheet + ); + var hp = player.Stats.HP; + var buff = Assert.IsType(Assert.Single(buffs)); + Assert.NotNull(buff.CustomField); + + // Equip item + var equipmentRow = + _tableSheets.EquipmentItemSheet.Values.Where(r => r.Stat.StatType == StatType.HP).OrderByDescending(r => r.Stat.TotalValue).First(); + var equipment = (Equipment)ItemFactory.CreateItem(equipmentRow, new TestRandom()); + player.Stats.SetEquipments(new[] { equipment }, _tableSheets.EquipmentItemSetEffectSheet); + Assert.True(player.Stats.HP > hp); + buffs = BuffFactory.GetBuffs( + player.Stats, + skill, + _tableSheets.SkillBuffSheet, + _tableSheets.StatBuffSheet, + _tableSheets.SkillActionBuffSheet, + _tableSheets.ActionBuffSheet + ); + var buff2 = Assert.IsType(Assert.Single(buffs)); + Assert.NotNull(buff2.CustomField); + Assert.True(buff2.CustomField.Value.BuffValue > buff.CustomField.Value.BuffValue); + hp = player.Stats.HP; + + // Arena buff + player.Stats.IncreaseHpForArena(); + Assert.True(player.Stats.HP > hp); + buffs = BuffFactory.GetBuffs( + player.Stats, + skill, + _tableSheets.SkillBuffSheet, + _tableSheets.StatBuffSheet, + _tableSheets.SkillActionBuffSheet, + _tableSheets.ActionBuffSheet + ); + var buff3 = Assert.IsType(Assert.Single(buffs)); + Assert.NotNull(buff3.CustomField); + Assert.True(buff3.CustomField.Value.BuffValue > buff2.CustomField.Value.BuffValue); + } } } diff --git a/Lib9c/Model/Stat/CharacterStats.cs b/Lib9c/Model/Stat/CharacterStats.cs index 783275065c..81484bf0d2 100644 --- a/Lib9c/Model/Stat/CharacterStats.cs +++ b/Lib9c/Model/Stat/CharacterStats.cs @@ -373,10 +373,28 @@ public void SetCostume(IEnumerable statModifiers) AddCostume(statModifiers); } + /// + /// Increases the HP of the character for the arena. + /// public void IncreaseHpForArena() { - var originalHP = _statMap[StatType.HP]; - _statMap[StatType.HP].SetBaseValue(Math.Max(0, originalHP.TotalValueAsLong * HpIncreasingModifier)); + var originalHp = _statMap[StatType.HP]; + var modifiedHp = CalculateModifiedBaseHp(originalHp); + + _statMap[StatType.HP].SetBaseValue(modifiedHp); + var modifiedStatHp = CalculateModifiedHpWithoutBuffs(modifiedHp); + + StatWithoutBuffs[StatType.HP].SetBaseValue(modifiedStatHp); + } + + private long CalculateModifiedBaseHp(DecimalStat originalHp) + { + return Math.Max(0, originalHp.TotalValueAsLong * HpIncreasingModifier); + } + + private long CalculateModifiedHpWithoutBuffs(long modifiedHp) + { + return Math.Max(0, modifiedHp - _buffStats.HP * HpIncreasingModifier); } private void UpdateBaseStats() From 26fc490c77663ddcad95fe1eb14b6268926151ad Mon Sep 17 00:00:00 2001 From: hyeon Date: Wed, 14 Feb 2024 01:15:01 +0900 Subject: [PATCH 48/64] Change test name to show test purpose --- .Lib9c.Tests/Model/StageSimulatorTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.Lib9c.Tests/Model/StageSimulatorTest.cs b/.Lib9c.Tests/Model/StageSimulatorTest.cs index a3ba50f62e..8aa5b4b79f 100644 --- a/.Lib9c.Tests/Model/StageSimulatorTest.cs +++ b/.Lib9c.Tests/Model/StageSimulatorTest.cs @@ -92,7 +92,7 @@ public void Simulate() } [Fact] - public void TestSkillSpeed() + public void TestSpeedModifierBySkill() { var equipmentRow = _tableSheets.EquipmentItemSheet.OrderedList.First(e => e.Id == 10114000); From 9ed4c1bc6b0a6a207876580c538f77ff4dc7116c Mon Sep 17 00:00:00 2001 From: hyeon Date: Wed, 14 Feb 2024 01:56:03 +0900 Subject: [PATCH 49/64] Apply speed modifier to arena --- .Lib9c.Tests/Model/ArenaSimulatorTest.cs | 93 +++++++++++++++++++++++- Lib9c/Arena/ArenaSimulator.cs | 13 +++- 2 files changed, 104 insertions(+), 2 deletions(-) diff --git a/.Lib9c.Tests/Model/ArenaSimulatorTest.cs b/.Lib9c.Tests/Model/ArenaSimulatorTest.cs index 84ed1a2627..eddd256cb8 100644 --- a/.Lib9c.Tests/Model/ArenaSimulatorTest.cs +++ b/.Lib9c.Tests/Model/ArenaSimulatorTest.cs @@ -4,14 +4,19 @@ namespace Lib9c.Tests using System.Linq; using Lib9c.Tests.Action; using Libplanet.Action; + using Libplanet.Crypto; + using Nekoyume.Action; using Nekoyume.Arena; using Nekoyume.Model; using Nekoyume.Model.BattleStatus.Arena; + using Nekoyume.Model.Item; using Nekoyume.Model.State; using Xunit; + using Xunit.Abstractions; public class ArenaSimulatorTest { + private readonly ITestOutputHelper _testOutputHelper; private readonly TableSheets _tableSheets; private readonly IRandom _random; private readonly AvatarState _avatarState1; @@ -20,8 +25,9 @@ public class ArenaSimulatorTest private readonly ArenaAvatarState _arenaAvatarState1; private readonly ArenaAvatarState _arenaAvatarState2; - public ArenaSimulatorTest() + public ArenaSimulatorTest(ITestOutputHelper testOutputHelper) { + _testOutputHelper = testOutputHelper; _tableSheets = new TableSheets(TableSheetsImporter.ImportSheets()); _random = new TestRandom(); @@ -120,5 +126,90 @@ public void HpIncreasingModifier(int? modifier) Assert.Equal(player.Stats.BaseHP * expectedHpModifier, player.CurrentHP); } } + + [Fact] + public void TestSpeedModifierBySkill() + { + // Unskilled + var equipmentRow = + _tableSheets.EquipmentItemSheet.OrderedList.First(e => e.Id == 10114000); + + var item1 = (Equipment)ItemFactory.CreateItem(equipmentRow, new TestRandom()); + Assert.Empty(item1.Skills); + item1.equipped = true; + _avatarState1.inventory.AddItem(item1); + + var item2 = (Equipment)ItemFactory.CreateItem(equipmentRow, new TestRandom()); + Assert.Empty(item2.Skills); + item2.equipped = true; + _avatarState2.inventory.AddItem(item2); + + var simulator = new ArenaSimulator(new TestRandom(), 10); + var arenaAvatarState1 = new ArenaAvatarState(_avatarState1); + arenaAvatarState1.Equipments.Add(item1.ItemId); + var arenaAvatarState2 = new ArenaAvatarState(_avatarState2); + arenaAvatarState2.Equipments.Add(item2.ItemId); + var myDigest = new ArenaPlayerDigest(_avatarState1, arenaAvatarState1); + var enemyDigest = new ArenaPlayerDigest(_avatarState2, arenaAvatarState2); + var arenaSheets = _tableSheets.GetArenaSimulatorSheets(); + var unskilledLog = simulator.Simulate(myDigest, enemyDigest, arenaSheets); + // foreach (var log in unskilledLog) + // { + // _testOutputHelper.WriteLine($"{log.Character.Id} :: {log}"); + // } + // + // _testOutputHelper.WriteLine("================================"); + + // Skilled + // Remove Items + _avatarState1.inventory.Equipments.First().equipped = false; + _avatarState1.inventory.RemoveNonFungibleItem(item1.ItemId); + Assert.Empty(_avatarState1.inventory.Equipments); + _avatarState2.inventory.Equipments.First().equipped = false; + _avatarState2.inventory.RemoveNonFungibleItem(item2.ItemId); + Assert.Empty(_avatarState2.inventory.Equipments); + + // Use skilled items + var skilledItem1 = (Equipment)ItemFactory.CreateItem(equipmentRow, new TestRandom()); + Assert.Empty(skilledItem1.Skills); + CombinationEquipment.AddSkillOption( + new AgentState(new PrivateKey().Address), + skilledItem1, + new TestRandom(0), + _tableSheets.EquipmentItemSubRecipeSheetV2.OrderedList!.First(r => r.Id == 10), + _tableSheets.EquipmentItemOptionSheet, + _tableSheets.SkillSheet + ); + Assert.True(skilledItem1.Skills.Any()); + skilledItem1.equipped = true; + _avatarState1.inventory.AddItem(skilledItem1); + var skilledItem2 = (Equipment)ItemFactory.CreateItem(equipmentRow, new TestRandom()); + Assert.Empty(skilledItem2.Skills); + CombinationEquipment.AddSkillOption( + new AgentState(new PrivateKey().Address), + skilledItem2, + new TestRandom(0), + _tableSheets.EquipmentItemSubRecipeSheetV2.OrderedList!.First(r => r.Id == 10), + _tableSheets.EquipmentItemOptionSheet, + _tableSheets.SkillSheet + ); + Assert.True(skilledItem2.Skills.Any()); + skilledItem2.equipped = true; + _avatarState2.inventory.AddItem(skilledItem2); + + simulator = new ArenaSimulator(new TestRandom(), 10); + arenaAvatarState1 = new ArenaAvatarState(_avatarState1); + arenaAvatarState1.Equipments.Add(skilledItem1.ItemId); + arenaAvatarState2 = new ArenaAvatarState(_avatarState2); + arenaAvatarState2.Equipments.Add(skilledItem2.ItemId); + myDigest = new ArenaPlayerDigest(_avatarState1, arenaAvatarState1); + enemyDigest = new ArenaPlayerDigest(_avatarState2, arenaAvatarState2); + arenaSheets = _tableSheets.GetArenaSimulatorSheets(); + var skilledLog = simulator.Simulate(myDigest, enemyDigest, arenaSheets); + // foreach (var log in skilledLog) + // { + // _testOutputHelper.WriteLine($"{log.Character.Id} :: {log}"); + // } + } } } diff --git a/Lib9c/Arena/ArenaSimulator.cs b/Lib9c/Arena/ArenaSimulator.cs index f06cd8ca8c..51fa0f8930 100644 --- a/Lib9c/Arena/ArenaSimulator.cs +++ b/Lib9c/Arena/ArenaSimulator.cs @@ -3,6 +3,7 @@ using Libplanet.Action; using Nekoyume.Model; using Nekoyume.Model.BattleStatus.Arena; +using Nekoyume.Model.Skill; using Nekoyume.TableData; using Priority_Queue; @@ -73,8 +74,18 @@ public ArenaLog Simulate( foreach (var other in players) { + var spdMultiplier = 0.6m; var current = players.GetPriority(other); - var speed = current * (other.usedSkill != null ? 0.9m : 0.6m); + var skills = sheets.SkillSheet.OrderedList.Select(s => s.SkillCategory); + var skillCategory = + other.usedSkill?.SkillInfos.Where(si => skills.Contains(si.SkillCategory)) + .Select(si => si.SkillCategory); + if (skillCategory != null && skillCategory.Any(sc => sc != SkillCategory.NormalAttack)) + { + spdMultiplier = 0.9m; + } + + var speed = current * spdMultiplier; players.UpdatePriority(other, speed); } From 2cfa110bbf149dda99c9242cd9c833a3404c69b9 Mon Sep 17 00:00:00 2001 From: hyeon Date: Wed, 14 Feb 2024 02:21:31 +0900 Subject: [PATCH 50/64] Apply speed modifier to raid --- .Lib9c.Tests/Model/RaidSimulatorV3Test.cs | 80 ++++++++++++++++++++++- Lib9c/Battle/RaidSimulator.cs | 12 +++- 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/.Lib9c.Tests/Model/RaidSimulatorV3Test.cs b/.Lib9c.Tests/Model/RaidSimulatorV3Test.cs index 843cda025e..2b0d114361 100644 --- a/.Lib9c.Tests/Model/RaidSimulatorV3Test.cs +++ b/.Lib9c.Tests/Model/RaidSimulatorV3Test.cs @@ -5,19 +5,25 @@ namespace Lib9c.Tests.Model using System.Linq; using Lib9c.Tests.Action; using Libplanet.Action; + using Libplanet.Crypto; + using Nekoyume.Action; using Nekoyume.Battle; using Nekoyume.Model.BattleStatus; + using Nekoyume.Model.Item; using Nekoyume.Model.State; using Xunit; + using Xunit.Abstractions; public class RaidSimulatorV3Test { + private readonly ITestOutputHelper _testOutputHelper; private readonly TableSheets _tableSheets; private readonly IRandom _random; private readonly AvatarState _avatarState; - public RaidSimulatorV3Test() + public RaidSimulatorV3Test(ITestOutputHelper testOutputHelper) { + _testOutputHelper = testOutputHelper; _tableSheets = new TableSheets(TableSheetsImporter.ImportSheets()); _random = new TestRandom(); @@ -61,5 +67,77 @@ public void Simulate() Assert.True(dead.Character.IsDead); } } + + [Fact] + public void TestSpeedMultiplierBySkill() + { + var bossId = _tableSheets.WorldBossListSheet.First().Value.BossId; + + // Unskilled + var equipmentRow = + _tableSheets.EquipmentItemSheet.OrderedList.First(e => e.Id == 10114000); + var item = (Equipment)ItemFactory.CreateItem(equipmentRow, new TestRandom()); + Assert.Empty(item.Skills); + item.equipped = true; + _avatarState.inventory.AddItem(item); + + var simulator = new RaidSimulator( + bossId, + new TestRandom(), + _avatarState, + new List(), + null, + _tableSheets.GetRaidSimulatorSheets(), + _tableSheets.CostumeStatSheet + ); + var player = simulator.Player; + var unskilledLogs = simulator.Simulate(); + var unSkilledActions = unskilledLogs.Where(l => l.Character?.Id == player.Id); + foreach (var log in unSkilledActions) + { + _testOutputHelper.WriteLine($"{log}"); + } + + _testOutputHelper.WriteLine("=========="); + + // Reset + _avatarState.inventory.Equipments.First().equipped = false; + _avatarState.inventory.RemoveNonFungibleItem(item.ItemId); + Assert.Empty(_avatarState.inventory.Equipments); + + // Skilled + var skilledItem = (Equipment)ItemFactory.CreateItem(equipmentRow, new TestRandom()); + Assert.Empty(skilledItem.Skills); + CombinationEquipment.AddSkillOption( + new AgentState(new PrivateKey().Address), + skilledItem, + new TestRandom(0), + _tableSheets.EquipmentItemSubRecipeSheetV2.OrderedList!.First(r => r.Id == 10), + _tableSheets.EquipmentItemOptionSheet, + _tableSheets.SkillSheet + ); + Assert.True(skilledItem.Skills.Any()); + skilledItem.equipped = true; + _avatarState.inventory.AddItem(skilledItem); + + simulator = new RaidSimulator( + bossId, + new TestRandom(), + _avatarState, + new List(), + null, + _tableSheets.GetRaidSimulatorSheets(), + _tableSheets.CostumeStatSheet + ); + player = simulator.Player; + var skilledLogs = simulator.Simulate(); + var skilledActions = skilledLogs.Where(l => l.Character?.Id == player.Id); + foreach (var log in skilledActions) + { + _testOutputHelper.WriteLine($"{log}"); + } + + Assert.True(skilledActions.Count() > unSkilledActions.Count()); + } } } diff --git a/Lib9c/Battle/RaidSimulator.cs b/Lib9c/Battle/RaidSimulator.cs index 475cab4aec..bf4b73ea6f 100644 --- a/Lib9c/Battle/RaidSimulator.cs +++ b/Lib9c/Battle/RaidSimulator.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Nekoyume.Model.Skill; namespace Nekoyume.Battle { @@ -159,8 +160,17 @@ public BattleLog Simulate() foreach (var other in Characters) { + var spdMultiplier = 0.6m; var current = Characters.GetPriority(other); - var speed = current * (other == Player && other.usedSkill != null ? 0.9m : 0.6m); + var skillRow = SkillSheet.OrderedList.FirstOrDefault(skill => + skill.Id == other.usedSkill?.SkillId); + if (other == Player && + skillRow?.SkillCategory != SkillCategory.NormalAttack) + { + spdMultiplier = 0.9m; + } + + var speed = current * spdMultiplier; Characters.UpdatePriority(other, speed); } From cff772e5e82ecfd0d093eb5c2faa0a8e90b1f57c Mon Sep 17 00:00:00 2001 From: hyeon Date: Wed, 14 Feb 2024 09:21:22 +0900 Subject: [PATCH 51/64] Apply collections to simulator tests --- .Lib9c.Tests/Model/ArenaSimulatorTest.cs | 4 ++-- .Lib9c.Tests/Model/RaidSimulatorV3Test.cs | 6 ++++-- .Lib9c.Tests/Model/StageSimulatorTest.cs | 6 ++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.Lib9c.Tests/Model/ArenaSimulatorTest.cs b/.Lib9c.Tests/Model/ArenaSimulatorTest.cs index 67aa35d4f3..9e1b162889 100644 --- a/.Lib9c.Tests/Model/ArenaSimulatorTest.cs +++ b/.Lib9c.Tests/Model/ArenaSimulatorTest.cs @@ -167,7 +167,7 @@ public void TestSpeedModifierBySkill() var myDigest = new ArenaPlayerDigest(_avatarState1, arenaAvatarState1); var enemyDigest = new ArenaPlayerDigest(_avatarState2, arenaAvatarState2); var arenaSheets = _tableSheets.GetArenaSimulatorSheets(); - var unskilledLog = simulator.Simulate(myDigest, enemyDigest, arenaSheets); + var unskilledLog = simulator.Simulate(myDigest, enemyDigest, arenaSheets, new List(), new List()); // foreach (var log in unskilledLog) // { // _testOutputHelper.WriteLine($"{log.Character.Id} :: {log}"); @@ -220,7 +220,7 @@ public void TestSpeedModifierBySkill() myDigest = new ArenaPlayerDigest(_avatarState1, arenaAvatarState1); enemyDigest = new ArenaPlayerDigest(_avatarState2, arenaAvatarState2); arenaSheets = _tableSheets.GetArenaSimulatorSheets(); - var skilledLog = simulator.Simulate(myDigest, enemyDigest, arenaSheets); + var skilledLog = simulator.Simulate(myDigest, enemyDigest, arenaSheets, new List(), new List()); // foreach (var log in skilledLog) // { // _testOutputHelper.WriteLine($"{log.Character.Id} :: {log}"); diff --git a/.Lib9c.Tests/Model/RaidSimulatorV3Test.cs b/.Lib9c.Tests/Model/RaidSimulatorV3Test.cs index 7a88db251e..73e70edb92 100644 --- a/.Lib9c.Tests/Model/RaidSimulatorV3Test.cs +++ b/.Lib9c.Tests/Model/RaidSimulatorV3Test.cs @@ -95,7 +95,8 @@ public void TestSpeedMultiplierBySkill() new List(), null, _tableSheets.GetRaidSimulatorSheets(), - _tableSheets.CostumeStatSheet + _tableSheets.CostumeStatSheet, + new List() ); var player = simulator.Player; var unskilledLogs = simulator.Simulate(); @@ -134,7 +135,8 @@ public void TestSpeedMultiplierBySkill() new List(), null, _tableSheets.GetRaidSimulatorSheets(), - _tableSheets.CostumeStatSheet + _tableSheets.CostumeStatSheet, + new List() ); player = simulator.Player; var skilledLogs = simulator.Simulate(); diff --git a/.Lib9c.Tests/Model/StageSimulatorTest.cs b/.Lib9c.Tests/Model/StageSimulatorTest.cs index f5f99f92c4..6c8132a2ec 100644 --- a/.Lib9c.Tests/Model/StageSimulatorTest.cs +++ b/.Lib9c.Tests/Model/StageSimulatorTest.cs @@ -126,7 +126,8 @@ public void TestSpeedModifierBySkill() StageSimulator.GetWaveRewards( new TestRandom(1), _tableSheets.StageSheet[3], - _tableSheets.MaterialItemSheet) + _tableSheets.MaterialItemSheet), + new List() ); var unskilledPlayer = simulator.Player; Assert.Contains(item, unskilledPlayer.Inventory.Equipments); @@ -177,7 +178,8 @@ public void TestSpeedModifierBySkill() StageSimulator.GetWaveRewards( new TestRandom(1), _tableSheets.StageSheet[3], - _tableSheets.MaterialItemSheet) + _tableSheets.MaterialItemSheet), + new List() ); var skilledPlayer = simulator.Player; Assert.Contains(skilledItem, skilledPlayer.Inventory.Equipments); From ccd118c6829a36552dec3476860f155c08a7bc04 Mon Sep 17 00:00:00 2001 From: hyeon Date: Wed, 14 Feb 2024 11:56:10 +0900 Subject: [PATCH 52/64] Fixation: Add missing brace --- .Lib9c.Tests/Model/ArenaSimulatorTest.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.Lib9c.Tests/Model/ArenaSimulatorTest.cs b/.Lib9c.Tests/Model/ArenaSimulatorTest.cs index 0aba53cb0c..e44515f03f 100644 --- a/.Lib9c.Tests/Model/ArenaSimulatorTest.cs +++ b/.Lib9c.Tests/Model/ArenaSimulatorTest.cs @@ -227,7 +227,8 @@ public void TestSpeedModifierBySkill() // { // _testOutputHelper.WriteLine($"{log.Character.Id} :: {log}"); // } - + } + [Fact] public void Thorns() { From 4ad1e83a0b937ba097283987ec1d457ec5ee325e Mon Sep 17 00:00:00 2001 From: hyeon Date: Wed, 14 Feb 2024 12:38:30 +0900 Subject: [PATCH 53/64] Check type of usedSkill directly, not using SkillSheet --- Lib9c/Arena/ArenaSimulator.cs | 7 +------ Lib9c/Battle/RaidSimulator.cs | 7 ++----- Lib9c/Battle/StageSimulator.cs | 7 ++----- 3 files changed, 5 insertions(+), 16 deletions(-) diff --git a/Lib9c/Arena/ArenaSimulator.cs b/Lib9c/Arena/ArenaSimulator.cs index 81b1614160..742ec5f511 100644 --- a/Lib9c/Arena/ArenaSimulator.cs +++ b/Lib9c/Arena/ArenaSimulator.cs @@ -3,7 +3,6 @@ using Libplanet.Action; using Nekoyume.Model; using Nekoyume.Model.BattleStatus.Arena; -using Nekoyume.Model.Skill; using Nekoyume.Model.Stat; using Nekoyume.TableData; using Priority_Queue; @@ -79,11 +78,7 @@ public ArenaLog Simulate( { var spdMultiplier = 0.6m; var current = players.GetPriority(other); - var skills = sheets.SkillSheet.OrderedList.Select(s => s.SkillCategory); - var skillCategory = - other.usedSkill?.SkillInfos.Where(si => skills.Contains(si.SkillCategory)) - .Select(si => si.SkillCategory); - if (skillCategory != null && skillCategory.Any(sc => sc != SkillCategory.NormalAttack)) + if (other.usedSkill is not null && other.usedSkill is not ArenaNormalAttack) { spdMultiplier = 0.9m; } diff --git a/Lib9c/Battle/RaidSimulator.cs b/Lib9c/Battle/RaidSimulator.cs index 15cf155ade..a3e1c22ac8 100644 --- a/Lib9c/Battle/RaidSimulator.cs +++ b/Lib9c/Battle/RaidSimulator.cs @@ -10,8 +10,8 @@ using System; using System.Collections.Generic; using System.Linq; -using Nekoyume.Model.Skill; using Nekoyume.Model.Stat; +using NormalAttack = Nekoyume.Model.BattleStatus.NormalAttack; namespace Nekoyume.Battle { @@ -166,10 +166,7 @@ public BattleLog Simulate() { var spdMultiplier = 0.6m; var current = Characters.GetPriority(other); - var skillRow = SkillSheet.OrderedList.FirstOrDefault(skill => - skill.Id == other.usedSkill?.SkillId); - if (other == Player && - skillRow?.SkillCategory != SkillCategory.NormalAttack) + if (other == Player && other.usedSkill is not null && other.usedSkill is not NormalAttack) { spdMultiplier = 0.9m; } diff --git a/Lib9c/Battle/StageSimulator.cs b/Lib9c/Battle/StageSimulator.cs index 972672cc86..e0806eadb5 100644 --- a/Lib9c/Battle/StageSimulator.cs +++ b/Lib9c/Battle/StageSimulator.cs @@ -10,9 +10,9 @@ using Nekoyume.Model.Stat; using Nekoyume.Model.State; using Nekoyume.Model.Buff; -using Nekoyume.Model.Skill; using Nekoyume.TableData; using Priority_Queue; +using NormalAttack = Nekoyume.Model.BattleStatus.NormalAttack; using Skill = Nekoyume.Model.Skill.Skill; namespace Nekoyume.Battle @@ -238,10 +238,7 @@ public Player Simulate() { var spdMultiplier = 0.6m; var current = Characters.GetPriority(other); - var skillRow = SkillSheet.OrderedList.FirstOrDefault(skill => - skill.Id == other.usedSkill?.SkillId); - - if (other == Player && skillRow?.SkillCategory != SkillCategory.NormalAttack) + if (other == Player && other.usedSkill is not null && other.usedSkill is not NormalAttack) { spdMultiplier = 0.9m; } From cee37bec076e2477a6a242bfc083b115a2f34b01 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Thu, 15 Feb 2024 19:13:08 +0900 Subject: [PATCH 54/64] Check equipment skill contains --- .Lib9c.Tests/TableData/CollectionSheetTest.cs | 56 ++++++++++++++++--- Lib9c/TableData/CollectionSheet.cs | 18 +++++- 2 files changed, 63 insertions(+), 11 deletions(-) diff --git a/.Lib9c.Tests/TableData/CollectionSheetTest.cs b/.Lib9c.Tests/TableData/CollectionSheetTest.cs index 70abe33add..47576e7c21 100644 --- a/.Lib9c.Tests/TableData/CollectionSheetTest.cs +++ b/.Lib9c.Tests/TableData/CollectionSheetTest.cs @@ -5,12 +5,16 @@ namespace Lib9c.Tests.TableData using Lib9c.Tests.Action; using Nekoyume.Model.Collection; using Nekoyume.Model.Item; + using Nekoyume.Model.Skill; using Nekoyume.Model.Stat; using Nekoyume.TableData; using Xunit; public class CollectionSheetTest { + private readonly TableSheets _tableSheets = + new TableSheets(TableSheetsImporter.ImportSheets()); + [Fact] public void Set() { @@ -44,11 +48,50 @@ public void Set() } [Theory] - [InlineData(ItemType.Equipment)] - [InlineData(ItemType.Costume)] - public void Validate(ItemType itemType) + [InlineData(true, 0, 0, false)] + [InlineData(true, 1, 0, true)] + [InlineData(true, 1, 1, true)] + [InlineData(true, 0, 1, true)] + [InlineData(false, 0, 0, true)] + [InlineData(false, 1, 0, false)] + [InlineData(false, 1, 1, false)] + [InlineData(false, 0, 1, false)] + public void Validate_Equipment(bool skillContains, int skillCount, int buffSkillCount, bool expected) { - var row = new TableSheets(TableSheetsImporter.ImportSheets()).ItemSheet.Values.First(r => r.ItemType == itemType); + var row = _tableSheets.ItemSheet.Values.First(r => r.ItemType == ItemType.Equipment); + var item = (Equipment)ItemFactory.CreateItem(row, new TestRandom()); + for (int i = 0; i < skillCount; i++) + { + var skillRow = _tableSheets.SkillSheet.Values.First(); + var skill = SkillFactory.Get(skillRow, 0, 0, 0, StatType.NONE); + item.Skills.Add(skill); + } + + for (int i = 0; i < buffSkillCount; i++) + { + var skillId = _tableSheets.SkillBuffSheet.Values.First().SkillId; + var buffSkillRow = _tableSheets.SkillSheet[skillId]; + var buffSkill = (BuffSkill)SkillFactory.Get(buffSkillRow, 0, 0, 0, StatType.NONE); + item.BuffSkills.Add(buffSkill); + } + + Assert.Equal(skillCount, item.Skills.Count); + + var materialInfo = new CollectionSheet.RequiredMaterial + { + ItemId = row.Id, + Count = 1, + Level = 0, + SkillContains = skillContains, + }; + + Assert.Equal(expected, materialInfo.Validate(item)); + } + + [Fact] + public void Validate_Costume() + { + var row = _tableSheets.ItemSheet.Values.First(r => r.ItemType == ItemType.Costume); var item = ItemFactory.CreateItem(row, new TestRandom()); var materialInfo = new CollectionSheet.RequiredMaterial { @@ -58,11 +101,6 @@ public void Validate(ItemType itemType) SkillContains = false, }; Assert.True(materialInfo.Validate((INonFungibleItem)item)); - if (item is Equipment equipment) - { - materialInfo.SkillContains = true; - Assert.False(materialInfo.Validate(equipment)); - } } [Fact] diff --git a/Lib9c/TableData/CollectionSheet.cs b/Lib9c/TableData/CollectionSheet.cs index 6f467a6430..8ac301acd8 100644 --- a/Lib9c/TableData/CollectionSheet.cs +++ b/Lib9c/TableData/CollectionSheet.cs @@ -20,8 +20,22 @@ public class RequiredMaterial private bool Validate(Equipment equipment) { - return equipment.Id == ItemId && equipment.level == Level && - (equipment.Skills.Any() == SkillContains || equipment.BuffSkills.Any() == SkillContains); + return equipment.Id == ItemId && equipment.level == Level && CheckSkill(equipment); + } + + /// + /// Checks if the given equipment has skills or buff skills based on the RequiredMaterial configuration. + /// + /// The equipment to check. + /// True if the equipment has skills or buff skills when SkillContains. otherwise equipment has no skills and buff skills. + private bool CheckSkill(Equipment equipment) + { + if (SkillContains) + { + return equipment.Skills.Any() || equipment.BuffSkills.Any(); + } + + return !equipment.Skills.Any() && !equipment.BuffSkills.Any(); } private bool Validate(Costume costume) From c8369b75b44c771d6a005e27ec46457dcf6a04ad Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Fri, 16 Feb 2024 13:47:51 +0900 Subject: [PATCH 55/64] Refactor Player set stats --- Lib9c/Battle/RaidSimulator.cs | 8 +------- Lib9c/Battle/StageSimulator.cs | 8 +------- Lib9c/Model/Character/Player.cs | 11 +++++++++++ 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/Lib9c/Battle/RaidSimulator.cs b/Lib9c/Battle/RaidSimulator.cs index a3e1c22ac8..c58dc530b1 100644 --- a/Lib9c/Battle/RaidSimulator.cs +++ b/Lib9c/Battle/RaidSimulator.cs @@ -38,13 +38,7 @@ public RaidSimulator( CostumeStatSheet costumeStatSheet, List collectionModifiers) : base(random, avatarState, foods, simulatorSheets) { - Player.SetCostumeStat(costumeStatSheet); - if (runeStates != null) - { - Player.SetRune(runeStates, simulatorSheets.RuneOptionSheet, simulatorSheets.SkillSheet); - } - - Player.Stats.SetCollections(collectionModifiers); + Player.ConfigureStats(costumeStatSheet, runeStates, simulatorSheets.RuneOptionSheet, simulatorSheets.SkillSheet, collectionModifiers); BossId = bossId; _waves = new List(); diff --git a/Lib9c/Battle/StageSimulator.cs b/Lib9c/Battle/StageSimulator.cs index e0806eadb5..a84c675d74 100644 --- a/Lib9c/Battle/StageSimulator.cs +++ b/Lib9c/Battle/StageSimulator.cs @@ -57,13 +57,7 @@ public StageSimulator(IRandom random, simulatorSheets, logEvent) { - Player.SetCostumeStat(costumeStatSheet); - if (runeStates != null) - { - Player.SetRune(runeStates, simulatorSheets.RuneOptionSheet, simulatorSheets.SkillSheet); - } - - Player.Stats.SetCollections(collectionModifiers); + Player.ConfigureStats(costumeStatSheet, runeStates, simulatorSheets.RuneOptionSheet, simulatorSheets.SkillSheet, collectionModifiers); _waves = new List(); _waveRewards = waveRewards; diff --git a/Lib9c/Model/Character/Player.cs b/Lib9c/Model/Character/Player.cs index 1252c26b35..8256ae18e6 100644 --- a/Lib9c/Model/Character/Player.cs +++ b/Lib9c/Model/Character/Player.cs @@ -604,6 +604,17 @@ public void SetRune( } } + public void ConfigureStats(CostumeStatSheet costumeStatSheet, List runeStates, RuneOptionSheet runeOptionSheet, SkillSheet skillSheet, List collectionModifiers) + { + SetCostumeStat(costumeStatSheet); + if (runeStates != null) + { + SetRune(runeStates, runeOptionSheet, skillSheet); + } + + Stats.SetCollections(collectionModifiers); + } + [Obsolete("Use SetRune")] public void SetRuneV1( List runes, From a5e7333de9d21b724212cc3bb4f9f2690cfed2ff Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Fri, 16 Feb 2024 14:48:54 +0900 Subject: [PATCH 56/64] Refactor ArenaCharacter set stats --- .Lib9c.Tests/Model/Skill/BuffFactoryTest.cs | 1 + Lib9c/Arena/ArenaSimulator.cs | 20 ++------------------ Lib9c/Model/Character/ArenaCharacter.cs | 10 ++++++++++ 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/.Lib9c.Tests/Model/Skill/BuffFactoryTest.cs b/.Lib9c.Tests/Model/Skill/BuffFactoryTest.cs index 2be42919c0..f5fad4e550 100644 --- a/.Lib9c.Tests/Model/Skill/BuffFactoryTest.cs +++ b/.Lib9c.Tests/Model/Skill/BuffFactoryTest.cs @@ -52,6 +52,7 @@ public void GetBuffs_Arena(bool setExtraValueBuffBeforeGetBuffs) digest, arenaSheets, simulator.HpModifier, + new List(), setExtraValueBuffBeforeGetBuffs: setExtraValueBuffBeforeGetBuffs); var buffs = BuffFactory.GetBuffs( challenger.Stats, diff --git a/Lib9c/Arena/ArenaSimulator.cs b/Lib9c/Arena/ArenaSimulator.cs index 742ec5f511..d2dd618869 100644 --- a/Lib9c/Arena/ArenaSimulator.cs +++ b/Lib9c/Arena/ArenaSimulator.cs @@ -121,33 +121,17 @@ private static SimplePriorityQueue SpawnPlayers( challengerDigest, simulatorSheets, simulator.HpModifier, + challengerCollectionModifiers, setExtraValueBuffBeforeGetBuffs: setExtraValueBuffBeforeGetBuffs); - if (challengerDigest.Runes != null) - { - challenger.SetRune( - challengerDigest.Runes, - simulatorSheets.RuneOptionSheet, - simulatorSheets.SkillSheet); - } - - challenger.Stats.SetCollections(challengerCollectionModifiers); var enemy = new ArenaCharacter( simulator, enemyDigest, simulatorSheets, simulator.HpModifier, + enemyCollectionModifiers, isEnemy: true, setExtraValueBuffBeforeGetBuffs: setExtraValueBuffBeforeGetBuffs); - if (enemyDigest.Runes != null) - { - enemy.SetRune( - enemyDigest.Runes, - simulatorSheets.RuneOptionSheet, - simulatorSheets.SkillSheet); - } - - enemy.Stats.SetCollections(enemyCollectionModifiers); challenger.Spawn(enemy); enemy.Spawn(challenger); diff --git a/Lib9c/Model/Character/ArenaCharacter.cs b/Lib9c/Model/Character/ArenaCharacter.cs index ddd241ac7f..89a45c1f91 100644 --- a/Lib9c/Model/Character/ArenaCharacter.cs +++ b/Lib9c/Model/Character/ArenaCharacter.cs @@ -157,6 +157,7 @@ public ArenaCharacter( ArenaPlayerDigest digest, ArenaSimulatorSheets sheets, long hpModifier, + List collectionModifiers, bool isEnemy = false, bool setExtraValueBuffBeforeGetBuffs = false) { @@ -186,6 +187,15 @@ public ArenaCharacter( _skills = GetSkills(digest.Equipments, sheets.SkillSheet); _attackCountMax = AttackCountHelper.GetCountMax(digest.Level); ResetCurrentHP(); + if (digest.Runes != null) + { + SetRune( + digest.Runes, + sheets.RuneOptionSheet, + sheets.SkillSheet); + } + + Stats.SetCollections(collectionModifiers); } private ArenaCharacter(ArenaCharacter value) From 2150195616cb8378c3582c8c4d4d500eb30ad1ac Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Fri, 16 Feb 2024 14:52:15 +0900 Subject: [PATCH 57/64] Refactor CharacterStats set stats --- Lib9c/Battle/CPHelper.cs | 9 +---- Lib9c/Model/Stat/CharacterStats.cs | 65 ++++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/Lib9c/Battle/CPHelper.cs b/Lib9c/Battle/CPHelper.cs index 4e560135a8..a11f853826 100644 --- a/Lib9c/Battle/CPHelper.cs +++ b/Lib9c/Battle/CPHelper.cs @@ -26,14 +26,7 @@ public static int TotalCP( { // Prepare CharacterStats for calculate collection Stats var characterStats = new CharacterStats(row, level); - characterStats.SetEquipments(equipments, new EquipmentItemSetEffectSheet()); - characterStats.SetCostumeStat(costumes, costumeStatSheet); - foreach (var runeOption in runeOptions) - { - characterStats.AddRuneStat(runeOption); - } - - characterStats.SetCollections(collectionStatModifiers); + characterStats.ConfigureStats(equipments, costumes, runeOptions, costumeStatSheet, collectionStatModifiers); foreach (var (statType, value) in characterStats.CollectionStats.GetStats()) { collectionCp += GetStatCP(statType, value); diff --git a/Lib9c/Model/Stat/CharacterStats.cs b/Lib9c/Model/Stat/CharacterStats.cs index 81484bf0d2..56e1af1377 100644 --- a/Lib9c/Model/Stat/CharacterStats.cs +++ b/Lib9c/Model/Stat/CharacterStats.cs @@ -101,6 +101,7 @@ public CharacterStats( { _initialStatModifiers.AddRange(initialStatModifiers); } + SetStats(level); } @@ -183,7 +184,8 @@ public CharacterStats SetEquipments( // set effects. var setEffectRows = sheet.GetSetEffectRows(equipments); - foreach (var statModifier in setEffectRows.SelectMany(row => row.StatModifiers.Values)) + foreach (var statModifier in setEffectRows.SelectMany(row => + row.StatModifiers.Values)) { _equipmentStatModifiers.Add(statModifier); } @@ -263,7 +265,8 @@ public CharacterStats SetRunes(IEnumerable value, bool updateImmed /// /// /// - public CharacterStats SetBuffs(IEnumerable value, bool updateImmediate = true) + public CharacterStats SetBuffs(IEnumerable value, + bool updateImmediate = true) { _buffStatModifiers.Clear(); if (!(value is null)) @@ -307,7 +310,8 @@ public CharacterStats SetCollections(IEnumerable statModifiers, { var statType = group.Key; var sum = group.Sum(g => g.Value); - _collectionStatModifiers.Add(new StatModifier(statType, StatModifier.OperationType.Percentage, sum)); + _collectionStatModifiers.Add(new StatModifier(statType, + StatModifier.OperationType.Percentage, sum)); } if (updateImmediate) @@ -361,9 +365,10 @@ private void SetCostumes(IEnumerable costumes, CostumeStatSheet costume var stat = costumeStatSheet.OrderedList .Where(r => r.CostumeId == costume.Id) .Select(row => new StatModifier(row.StatType, StatModifier.OperationType.Add, - (int)row.Stat)); + (int) row.Stat)); statModifiers.AddRange(stat); } + SetCostume(statModifiers); } @@ -455,23 +460,28 @@ private void UpdateBuffStats() { var statType = group.Key; var sum = group.Sum(g => g.Value); - buffModifiers.Add(new StatModifier(statType, StatModifier.OperationType.Percentage, sum)); + buffModifiers.Add(new StatModifier(statType, StatModifier.OperationType.Percentage, + sum)); } - _buffStats.Set(buffModifiers, _baseStats, _equipmentStats, _consumableStats, _runeStats, _costumeStats, _collectionStats); + _buffStats.Set(buffModifiers, _baseStats, _equipmentStats, _consumableStats, _runeStats, + _costumeStats, _collectionStats); UpdateTotalStats(); } private void UpdateCostumeStats() { - _costumeStats.Set(_costumeStatModifiers, _baseStats, _equipmentStats, _consumableStats, _runeStats); + _costumeStats.Set(_costumeStatModifiers, _baseStats, _equipmentStats, _consumableStats, + _runeStats); UpdateCollectionStats(); } private void UpdateCollectionStats() { - _collectionStats.Set(_collectionStatModifiers, _baseStats, _equipmentStats, _consumableStats, _runeStats, _costumeStats); - Set(StatWithoutBuffs, _baseStats, _equipmentStats, _consumableStats, _runeStats, _costumeStats, _collectionStats); + _collectionStats.Set(_collectionStatModifiers, _baseStats, _equipmentStats, + _consumableStats, _runeStats, _costumeStats); + Set(StatWithoutBuffs, _baseStats, _equipmentStats, _consumableStats, _runeStats, + _costumeStats, _collectionStats); foreach (var stat in StatWithoutBuffs.GetDecimalStats(false)) { if (!LegacyDecimalStatTypes.Contains(stat.StatType)) @@ -491,7 +501,8 @@ private void UpdateCollectionStats() private void UpdateTotalStats() { - Set(_statMap, _baseStats, _equipmentStats, _consumableStats, _runeStats, _costumeStats, _collectionStats, _buffStats); + Set(_statMap, _baseStats, _equipmentStats, _consumableStats, _runeStats, _costumeStats, + _collectionStats, _buffStats); foreach (var stat in _statMap.GetDecimalStats(false)) { @@ -519,12 +530,14 @@ public override object Clone() return new CharacterStats(this); } - public IEnumerable<(StatType statType, long baseValue)> GetBaseStats(bool ignoreZero = false) + public IEnumerable<(StatType statType, long baseValue)> GetBaseStats( + bool ignoreZero = false) { return _baseStats.GetStats(ignoreZero); } - public IEnumerable<(StatType statType, long additionalValue)> GetAdditionalStats(bool ignoreZero = false) + public IEnumerable<(StatType statType, long additionalValue)> GetAdditionalStats( + bool ignoreZero = false) { var baseStats = _baseStats.GetStats(); foreach (var (statType, stat) in baseStats) @@ -537,8 +550,9 @@ public override object Clone() } } - public IEnumerable<(StatType statType, long baseValue, long additionalValue)> GetBaseAndAdditionalStats( - bool ignoreZero = false) + public IEnumerable<(StatType statType, long baseValue, long additionalValue)> + GetBaseAndAdditionalStats( + bool ignoreZero = false) { var additionalStats = GetAdditionalStats(); foreach (var (statType, additionalStat) in additionalStats) @@ -552,7 +566,8 @@ public override object Clone() } } - public void SetCostumeStat(IReadOnlyCollection costumes, CostumeStatSheet costumeStatSheet) + public void SetCostumeStat(IReadOnlyCollection costumes, + CostumeStatSheet costumeStatSheet) { var statModifiers = new List(); foreach (var itemId in costumes.Select(costume => costume.Id)) @@ -560,9 +575,11 @@ public void SetCostumeStat(IReadOnlyCollection costumes, CostumeStatShe statModifiers.AddRange( costumeStatSheet.OrderedList .Where(r => r.CostumeId == itemId) - .Select(row => new StatModifier(row.StatType, StatModifier.OperationType.Add, (int) row.Stat)) + .Select(row => new StatModifier(row.StatType, + StatModifier.OperationType.Add, (int) row.Stat)) ); } + SetCostume(statModifiers); } @@ -577,5 +594,21 @@ public void AddRuneStat(RuneOptionSheet.Row.RuneOptionInfo optionInfo) x.stat.BaseValueAsLong))); AddRune(statModifiers); } + + public void ConfigureStats( + IReadOnlyCollection equipments, + IReadOnlyCollection costumes, + IReadOnlyCollection runeOptions, + CostumeStatSheet costumeStatSheet, List collectionStatModifiers) + { + SetEquipments(equipments, new EquipmentItemSetEffectSheet()); + SetCostumeStat(costumes, costumeStatSheet); + foreach (var runeOption in runeOptions) + { + AddRuneStat(runeOption); + } + + SetCollections(collectionStatModifiers); + } } } From 3f1e02d094bf1453490fba57d393dac46b32edc5 Mon Sep 17 00:00:00 2001 From: tyrosine1153 Date: Mon, 19 Feb 2024 14:01:58 +0900 Subject: [PATCH 58/64] add tablesheet meta file --- Lib9c/TableCSV/CollectionSheet.csv.meta | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 Lib9c/TableCSV/CollectionSheet.csv.meta diff --git a/Lib9c/TableCSV/CollectionSheet.csv.meta b/Lib9c/TableCSV/CollectionSheet.csv.meta new file mode 100644 index 0000000000..3ebb310c27 --- /dev/null +++ b/Lib9c/TableCSV/CollectionSheet.csv.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e458af4b236c44b48b0d7a0f20f0acfb +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From 02131d7e76bb1a6fd4abf0e62b846a82233c72a3 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Tue, 20 Feb 2024 14:09:42 +0900 Subject: [PATCH 59/64] Bump libplanet to use a custom version --- .Libplanet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.Libplanet b/.Libplanet index a969550f22..94e15d88db 160000 --- a/.Libplanet +++ b/.Libplanet @@ -1 +1 @@ -Subproject commit a969550f22b6469ff94ac25dbf5d585028d3bded +Subproject commit 94e15d88dbeabdb0d3254ec0f3b491e4baf48109 From 8f610bc8c85123f901e8c68594b2cbe4f796a9c8 Mon Sep 17 00:00:00 2001 From: area363 Date: Tue, 20 Feb 2024 14:29:19 +0900 Subject: [PATCH 60/64] restore maxtxperblock --- Lib9c.Policy/Policy/BlockPolicySource.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c.Policy/Policy/BlockPolicySource.cs b/Lib9c.Policy/Policy/BlockPolicySource.cs index 0d5b42b3f3..edd16ab940 100644 --- a/Lib9c.Policy/Policy/BlockPolicySource.cs +++ b/Lib9c.Policy/Policy/BlockPolicySource.cs @@ -31,7 +31,7 @@ namespace Nekoyume.Blockchain.Policy { public partial class BlockPolicySource { - public const int MaxTransactionsPerBlock = 80; + public const int MaxTransactionsPerBlock = 200; public static readonly TimeSpan BlockInterval = TimeSpan.FromSeconds(8); From d30df860c02b4aae8a4e00a569a0001be3b3b09b Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Wed, 21 Feb 2024 11:56:54 +0900 Subject: [PATCH 61/64] Add block index validation to NonFungibleItem --- .../NonFungibleCollectionMaterialTest.cs | 18 ++++++++++++------ .Lib9c.Tests/Model/Item/InventoryTest.cs | 17 +++++++++++++++++ Lib9c/Action/ActivateCollection.cs | 2 +- .../NonFungibleCollectionMaterial.cs | 7 +++++-- Lib9c/Model/Item/Inventory.cs | 14 ++++++++++++-- 5 files changed, 47 insertions(+), 11 deletions(-) diff --git a/.Lib9c.Tests/Model/Collection/NonFungibleCollectionMaterialTest.cs b/.Lib9c.Tests/Model/Collection/NonFungibleCollectionMaterialTest.cs index c27dd109ab..b16b7f1842 100644 --- a/.Lib9c.Tests/Model/Collection/NonFungibleCollectionMaterialTest.cs +++ b/.Lib9c.Tests/Model/Collection/NonFungibleCollectionMaterialTest.cs @@ -1,5 +1,6 @@ namespace Lib9c.Tests.Model.Collection { + using System; using System.Linq; using Lib9c.Tests.Action; using Nekoyume.Action; @@ -36,14 +37,16 @@ public void BurnMaterial(ItemType itemType) ItemCount = 1, NonFungibleId = nonfungibleId, }; - nonFungibleCollectionMaterial.BurnMaterial(row, inventory, materialInfo); + nonFungibleCollectionMaterial.BurnMaterial(row, inventory, materialInfo, 0L); Assert.Empty(inventory.Items); } [Theory] - [InlineData(ItemType.Equipment)] - [InlineData(ItemType.Costume)] - public void BurnMaterial_ItemDoesNotExistException(ItemType itemType) + [InlineData(ItemType.Equipment, "6856AE42-A820-4041-92B0-5D7BAA52F2AA", 100L, 10L)] + [InlineData(ItemType.Costume, "701BA698-CCB9-4FC7-B88F-7CB8C707D135", 100L, 99L)] + [InlineData(ItemType.Equipment, "6f460c1a-755d-48e4-ad67-65d5f519dbc8", 11L, 10L)] + [InlineData(ItemType.Costume, "6f460c1a-755d-48e4-ad67-65d5f519dbc8", 1L, 0L)] + public void BurnMaterial_ItemDoesNotExistException(ItemType itemType, Guid nonFungibleId, long requiredBlockIndex, long blockIndex) { var inventory = new Inventory(); var row = _tableSheets.ItemSheet.Values.First(r => r.ItemType == itemType); @@ -55,14 +58,17 @@ public void BurnMaterial_ItemDoesNotExistException(ItemType itemType) SkillContains = false, }; var item = ItemFactory.CreateItem(row, new TestRandom()); + var tradableItem = (ITradableItem)item; + tradableItem.RequiredBlockIndex = requiredBlockIndex; inventory.AddItem(item); Assert.Single(inventory.Items); var fungibleMaterial = new NonFungibleCollectionMaterial { ItemId = row.Id, ItemCount = 1, + NonFungibleId = nonFungibleId, }; - Assert.Throws(() => fungibleMaterial.BurnMaterial(row, inventory, materialInfo)); + Assert.Throws(() => fungibleMaterial.BurnMaterial(row, inventory, materialInfo, blockIndex)); } [Theory] @@ -88,7 +94,7 @@ public void BurnMaterial_InvalidItemTypeException(ItemType itemType) ItemId = row.Id, ItemCount = 1, }; - Assert.Throws(() => nonFungibleMaterial.BurnMaterial(row, inventory, materialInfo)); + Assert.Throws(() => nonFungibleMaterial.BurnMaterial(row, inventory, materialInfo, 0L)); } } } diff --git a/.Lib9c.Tests/Model/Item/InventoryTest.cs b/.Lib9c.Tests/Model/Item/InventoryTest.cs index e4ce2d2a8b..83dee74168 100644 --- a/.Lib9c.Tests/Model/Item/InventoryTest.cs +++ b/.Lib9c.Tests/Model/Item/InventoryTest.cs @@ -978,6 +978,23 @@ public void RemoveConsumable() Assert.Empty(inventory.Items); } + [Fact] + public void TryGetNonFungibleItem() + { + var equipment = GetFirstEquipment(); + Assert.Equal(0L, equipment.RequiredBlockIndex); + var inventory = new Inventory(); + inventory.AddItem(equipment); + // True because default blockIndex is long.MaxValue + Assert.True(inventory.TryGetNonFungibleItem(equipment.NonFungibleId, out Equipment item)); + Assert.Equal(equipment, item); + + equipment.RequiredBlockIndex = 10L; + inventory = new Inventory(); + inventory.AddItem(equipment); + Assert.False(inventory.TryGetNonFungibleItem(equipment.NonFungibleId, out Equipment _, 1L)); + } + private static Consumable GetFirstConsumable() { var row = TableSheets.ConsumableItemSheet.First; diff --git a/Lib9c/Action/ActivateCollection.cs b/Lib9c/Action/ActivateCollection.cs index a3abcd330a..10c3505077 100644 --- a/Lib9c/Action/ActivateCollection.cs +++ b/Lib9c/Action/ActivateCollection.cs @@ -60,7 +60,7 @@ public override IWorld Execute(IActionContext context) fungibleCollectionMaterial.BurnMaterial(itemRow, avatarState.inventory, context.BlockIndex); break; case NonFungibleCollectionMaterial nonFungibleCollectionMaterial: - nonFungibleCollectionMaterial.BurnMaterial(itemRow, avatarState.inventory, requiredMaterial); + nonFungibleCollectionMaterial.BurnMaterial(itemRow, avatarState.inventory, requiredMaterial, context.BlockIndex); break; default: throw new ArgumentOutOfRangeException(nameof(registeredMaterial)); diff --git a/Lib9c/Model/Collection/NonFungibleCollectionMaterial.cs b/Lib9c/Model/Collection/NonFungibleCollectionMaterial.cs index ef16993881..b80747e9fb 100644 --- a/Lib9c/Model/Collection/NonFungibleCollectionMaterial.cs +++ b/Lib9c/Model/Collection/NonFungibleCollectionMaterial.cs @@ -44,16 +44,19 @@ public NonFungibleCollectionMaterial() /// The object representing the item. /// The object representing the player's inventory. /// The object representing the material info. + /// The block index where the burn operation is taking place. /// Thrown when the material item does not exist in the inventory. /// Thrown when the item type is not supported by . - public void BurnMaterial(ItemSheet.Row itemRow, Inventory inventory, CollectionSheet.RequiredMaterial materialInfo) + public void BurnMaterial(ItemSheet.Row itemRow, Inventory inventory, + CollectionSheet.RequiredMaterial materialInfo, long blockIndex) { switch (itemRow.ItemType) { case ItemType.Costume: case ItemType.Equipment: if (inventory.TryGetNonFungibleItem(NonFungibleId, - out INonFungibleItem materialItem) && materialInfo.Validate(materialItem)) + out INonFungibleItem materialItem, blockIndex) && + materialInfo.Validate(materialItem)) { inventory.RemoveNonFungibleItem(materialItem); } diff --git a/Lib9c/Model/Item/Inventory.cs b/Lib9c/Model/Item/Inventory.cs index edfe3a6175..d34c04eba1 100644 --- a/Lib9c/Model/Item/Inventory.cs +++ b/Lib9c/Model/Item/Inventory.cs @@ -769,14 +769,24 @@ public bool TryGetNonFungibleItem(T nonFungibleItem, out T outNonFungibleItem where T : INonFungibleItem => TryGetNonFungibleItem(nonFungibleItem.NonFungibleId, out outNonFungibleItem); - public bool TryGetNonFungibleItem(Guid itemId, out T outNonFungibleItem) + /// + /// Tries to get a non-fungible item from the inventory with the specified item ID. + /// + /// The type of non-fungible item to retrieve. + /// The ID of the non-fungible item to retrieve. + /// When this method returns, contains the non-fungible item with the specified ID, if found; otherwise, the default value for the type. + /// The block index for the non-fungible item to be considered valid. Defaults to . + /// true if a non-fungible item with the specified ID is found in the inventory; otherwise, false. + public bool TryGetNonFungibleItem(Guid itemId, out T outNonFungibleItem, + long blockIndex = long.MaxValue) where T : INonFungibleItem { foreach (var item in _items) { if (item.Locked || !(item.item is T nonFungibleItem) || - !nonFungibleItem.NonFungibleId.Equals(itemId)) + !nonFungibleItem.NonFungibleId.Equals(itemId) || + nonFungibleItem.RequiredBlockIndex > blockIndex) { continue; } From 46292bde3e0d26aba50a85d15ec4c9e00b632c1f Mon Sep 17 00:00:00 2001 From: ilgyu Date: Fri, 23 Feb 2024 12:07:08 +0900 Subject: [PATCH 62/64] bump: Libplanet 4.0.6 --- .Libplanet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.Libplanet b/.Libplanet index 94e15d88db..1016fbce88 160000 --- a/.Libplanet +++ b/.Libplanet @@ -1 +1 @@ -Subproject commit 94e15d88dbeabdb0d3254ec0f3b491e4baf48109 +Subproject commit 1016fbce882309452a45eda1a19c9a8b213801a5 From 458ebffb200100ef24bd749194d520c350781b3d Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Mon, 26 Feb 2024 15:24:49 +0900 Subject: [PATCH 63/64] Fix GetCollectionState --- Lib9c/Module/CollectionModule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib9c/Module/CollectionModule.cs b/Lib9c/Module/CollectionModule.cs index cde5fd0030..568b8a2f54 100644 --- a/Lib9c/Module/CollectionModule.cs +++ b/Lib9c/Module/CollectionModule.cs @@ -24,7 +24,7 @@ public static class CollectionModule /// Thrown when the serialized Collection state is not in the correct format. public static CollectionState GetCollectionState(this IWorldState worldState, Address address) { - var serializedCollection = worldState.GetResolvedState(address, Addresses.Collection); + var serializedCollection = worldState.GetAccountState(Addresses.Collection).GetState(address); if (serializedCollection is null) { var msg = $"No Collection state ({address.ToHex()})"; From acc11654a2b3b35ddca574422015d74399d90232 Mon Sep 17 00:00:00 2001 From: Yang Chun Ung Date: Mon, 26 Feb 2024 15:39:11 +0900 Subject: [PATCH 64/64] Fix CollectionStates --- .Lib9c.Tests/Module/CollectionModuleTest.cs | 19 ++++------------ Lib9c/Action/BattleArena.cs | 24 +++++++++------------ Lib9c/Module/CollectionModule.cs | 24 ++++++++++++--------- 3 files changed, 28 insertions(+), 39 deletions(-) diff --git a/.Lib9c.Tests/Module/CollectionModuleTest.cs b/.Lib9c.Tests/Module/CollectionModuleTest.cs index 984988bf99..b3ea162800 100644 --- a/.Lib9c.Tests/Module/CollectionModuleTest.cs +++ b/.Lib9c.Tests/Module/CollectionModuleTest.cs @@ -42,8 +42,7 @@ public void CollectionStates() var address2 = new PrivateKey().Address; var addresses = new[] { address, address2 }; var result = states.GetCollectionStates(addresses); - Assert.Equal(addresses.Length, result.Count); - Assert.All(result, Assert.Null); + Assert.Empty(result); var state = new CollectionState { @@ -54,19 +53,9 @@ public void CollectionStates() }; states = states.SetCollectionState(address, state); result = states.GetCollectionStates(addresses); - for (int i = 0; i < addresses.Length; i++) - { - switch (i) - { - case 0: - Assert.NotNull(result[i]); - Assert.Equal(state.Ids, result[i].Ids); - break; - case 1: - Assert.Null(result[i]); - break; - } - } + Assert.Contains(address, result.Keys); + Assert.Equal(state.Ids, result[address].Ids); + Assert.DoesNotContain(address2, result.Keys); } } } diff --git a/Lib9c/Action/BattleArena.cs b/Lib9c/Action/BattleArena.cs index 93cffd088c..407d99f5d5 100644 --- a/Lib9c/Action/BattleArena.cs +++ b/Lib9c/Action/BattleArena.cs @@ -120,7 +120,7 @@ public override IWorld Execute(IActionContext context) var collectionStates = states.GetCollectionStates(new[]{ myAvatarAddress, enemyAvatarAddress }); - var collectionExist = collectionStates.Any(r => r is not null); + var collectionExist = collectionStates.Count > 0; var sheetTypes = new List { typeof(ArenaSheet), @@ -383,23 +383,19 @@ public override IWorld Execute(IActionContext context) var defeatCount = 0; var rewards = new List(); var random = context.GetRandom(); - var modifiers = new List> + var modifiers = new Dictionary> { - new(), - new(), + [myAvatarAddress] = new(), + [enemyAvatarAddress] = new(), }; if (collectionExist) { var collectionSheet = sheets.GetSheet(); - for (int i = 0; i < collectionStates.Count; i++) +#pragma warning disable LAA1002 + foreach (var (address, state) in collectionStates) +#pragma warning restore LAA1002 { - var state = collectionStates[i]; - if (state is null) - { - continue; - } - - var modifier = modifiers[i]; + var modifier = modifiers[address]; foreach (var collectionId in state.Ids) { modifier.AddRange(collectionSheet[collectionId].StatModifiers); @@ -413,8 +409,8 @@ public override IWorld Execute(IActionContext context) myArenaPlayerDigest, enemyArenaPlayerDigest, arenaSheets, - modifiers[0], - modifiers[1], + modifiers[myAvatarAddress], + modifiers[enemyAvatarAddress], true); if (log.Result.Equals(ArenaLog.ArenaResult.Win)) { diff --git a/Lib9c/Module/CollectionModule.cs b/Lib9c/Module/CollectionModule.cs index 568b8a2f54..ad8f3207a2 100644 --- a/Lib9c/Module/CollectionModule.cs +++ b/Lib9c/Module/CollectionModule.cs @@ -92,21 +92,25 @@ public static bool TryGetCollectionState(this IWorldState worldState, Address ad /// /// The world state used to retrieve the collection states. /// The list of addresses to retrieve the collection states for. - /// A list of CollectionState objects representing the collection states for the given addresses, - /// or null for addresses that do not have a collection state. - public static List GetCollectionStates(this IWorldState worldState, + /// A dictionary of Address and CollectionState pairs representing the collection states + /// for the given addresses, + /// or an empty dictionary for addresses that do not have a collection state. + public static Dictionary GetCollectionStates( + this IWorldState worldState, IReadOnlyList
addresses) { - var result = new List(); - foreach (var serialized in worldState.GetAccountState(Addresses.Collection).GetStates(addresses)) + var result = new Dictionary(); + IReadOnlyList values = + worldState + .GetAccountState(Addresses.Collection) + .GetStates(addresses); + for (int i = 0; i < addresses.Count; i++) { + var serialized = values[i]; + var address = addresses[i]; if (serialized is List bencoded) { - result.Add(new CollectionState(bencoded)); - } - else - { - result.Add(null); + result.TryAdd(address, new CollectionState(bencoded)); } }