From 4817a5be404eef1daeecdfd02102ab9be81542cb Mon Sep 17 00:00:00 2001 From: hyeon Date: Tue, 12 Mar 2024 17:41:46 +0900 Subject: [PATCH 01/17] Add shatter_strike_max_damage to GameConfig --- Lib9c/Model/State/GameConfigState.cs | 17 +++++++++++++++++ Lib9c/TableCSV/GameConfigSheet.csv | 1 + 2 files changed, 18 insertions(+) diff --git a/Lib9c/Model/State/GameConfigState.cs b/Lib9c/Model/State/GameConfigState.cs index a30d9deb69..5c5435c74c 100644 --- a/Lib9c/Model/State/GameConfigState.cs +++ b/Lib9c/Model/State/GameConfigState.cs @@ -53,6 +53,7 @@ public class GameConfigState : State public int RequireCharacterLevel_ConsumableSlot3 { get; private set; } public int RequireCharacterLevel_ConsumableSlot4 { get; private set; } public int RequireCharacterLevel_ConsumableSlot5 { get; private set; } + public long ShatterStrikeMaxDamage { get; private set; } public GameConfigState() : base(Address) { @@ -243,6 +244,11 @@ public GameConfigState(Dictionary serialized) : base(serialized) { RuneSkillSlotCrystalUnlockCost = (Integer)rscc2; } + + if (serialized.TryGetValue((Text)"shatter_strike_max_damage", out var ssmd)) + { + ShatterStrikeMaxDamage = ssmd.ToLong(); + } } public GameConfigState(string csv) : base(Address) @@ -471,6 +477,14 @@ public override IValue Serialize() (Integer)RuneStatSlotCrystalUnlockCost); } + if (ShatterStrikeMaxDamage > 0) + { + values.Add( + (Text)"shatter_strike_max_damage", + ShatterStrikeMaxDamage.Serialize() + ); + } + #pragma warning disable LAA1002 return new Dictionary(values.Union((Dictionary) base.Serialize())); #pragma warning restore LAA1002 @@ -630,6 +644,9 @@ public void Update(GameConfigSheet.Row row) RequireCharacterLevel_ConsumableSlot5 = TableExtensions.ParseInt(row.Value); break; + case "shatter_strike_max_damage": + ShatterStrikeMaxDamage = TableExtensions.ParseLong(row.Value); + break; } } } diff --git a/Lib9c/TableCSV/GameConfigSheet.csv b/Lib9c/TableCSV/GameConfigSheet.csv index 29cd53f513..1f0d237d79 100644 --- a/Lib9c/TableCSV/GameConfigSheet.csv +++ b/Lib9c/TableCSV/GameConfigSheet.csv @@ -36,3 +36,4 @@ character_consumable_slot_2,35 character_consumable_slot_3,100 character_consumable_slot_4,200 character_consumable_slot_5,350 +shatter_strike_max_damage,400000 From f5bf263856b5b8714272d3e55ebf8be18103a689 Mon Sep 17 00:00:00 2001 From: hyeon Date: Tue, 12 Mar 2024 18:41:01 +0900 Subject: [PATCH 02/17] Add GameConfigSheet to simulators --- .Lib9c.Tests/TableSheets.cs | 15 ++++++++----- Lib9c/Extensions/SheetsExtensions.cs | 15 ++++++++----- Lib9c/Module/LegacyModule.cs | 10 +++++++-- Lib9c/TableData/SimulatorSheets.cs | 33 ++++++++++++++++++---------- 4 files changed, 50 insertions(+), 23 deletions(-) diff --git a/.Lib9c.Tests/TableSheets.cs b/.Lib9c.Tests/TableSheets.cs index 67d5497355..cbbadbe683 100644 --- a/.Lib9c.Tests/TableSheets.cs +++ b/.Lib9c.Tests/TableSheets.cs @@ -285,7 +285,8 @@ public SimulatorSheets GetSimulatorSheets() CharacterSheet, CharacterLevelSheet, EquipmentItemSetEffectSheet, - RuneOptionSheet + RuneOptionSheet, + GameConfigSheet ); } @@ -319,7 +320,8 @@ public StageSimulatorSheets GetStageSimulatorSheets() StageSheet, StageWaveSheet, EnemySkillSheet, - RuneOptionSheet + RuneOptionSheet, + GameConfigSheet ); } @@ -370,7 +372,8 @@ public RankingSimulatorSheets GetRankingSimulatorSheets() CharacterLevelSheet, EquipmentItemSetEffectSheet, WeeklyArenaRewardSheet, - RuneOptionSheet + RuneOptionSheet, + GameConfigSheet ); } @@ -405,7 +408,8 @@ public ArenaSimulatorSheets GetArenaSimulatorSheets() EquipmentItemSetEffectSheet, CostumeStatSheet, WeeklyArenaRewardSheet, - RuneOptionSheet + RuneOptionSheet, + GameConfigSheet ); } @@ -446,7 +450,8 @@ public RaidSimulatorSheets GetRaidSimulatorSheets() WorldBossBattleRewardSheet, RuneWeightSheet, RuneSheet, - RuneOptionSheet + RuneOptionSheet, + GameConfigSheet ); } diff --git a/Lib9c/Extensions/SheetsExtensions.cs b/Lib9c/Extensions/SheetsExtensions.cs index 27bf6b069f..b604e933ae 100644 --- a/Lib9c/Extensions/SheetsExtensions.cs +++ b/Lib9c/Extensions/SheetsExtensions.cs @@ -191,7 +191,8 @@ public static SimulatorSheets GetSimulatorSheets( sheets.GetSheet(), sheets.GetSheet(), sheets.GetSheet(), - sheets.GetSheet() + sheets.GetSheet(), + sheets.GetSheet() ); } @@ -244,7 +245,8 @@ public static StageSimulatorSheets GetStageSimulatorSheets( sheets.GetSheet(), sheets.GetSheet(), sheets.GetSheet(), - sheets.GetSheet() + sheets.GetSheet(), + sheets.GetSheet() ); } @@ -296,7 +298,8 @@ public static RankingSimulatorSheets GetRankingSimulatorSheets( sheets.GetSheet(), sheets.GetSheet(), sheets.GetSheet(), - sheets.GetSheet() + sheets.GetSheet(), + sheets.GetSheet() ); } @@ -348,7 +351,8 @@ public static ArenaSimulatorSheets GetArenaSimulatorSheets( sheets.GetSheet(), sheets.GetSheet(), sheets.GetSheet(), - sheets.GetSheet() + sheets.GetSheet(), + sheets.GetSheet() ); } @@ -407,7 +411,8 @@ public static RaidSimulatorSheets GetRaidSimulatorSheets( sheets.GetSheet(), sheets.GetSheet(), sheets.GetSheet(), - sheets.GetSheet() + sheets.GetSheet(), + sheets.GetSheet() ); } diff --git a/Lib9c/Module/LegacyModule.cs b/Lib9c/Module/LegacyModule.cs index d4313b3577..f4fa1317a9 100644 --- a/Lib9c/Module/LegacyModule.cs +++ b/Lib9c/Module/LegacyModule.cs @@ -566,6 +566,7 @@ public static bool TryGetSheet(this IWorldState worldState, Address address, sheetTypeList.Add(typeof(CharacterLevelSheet)); sheetTypeList.Add(typeof(EquipmentItemSetEffectSheet)); sheetTypeList.Add(typeof(RuneOptionSheet)); + sheetTypeList.Add(typeof(GameConfigSheet)); } if (containStageSimulatorSheets) @@ -583,6 +584,7 @@ public static bool TryGetSheet(this IWorldState worldState, Address address, sheetTypeList.Add(typeof(StageWaveSheet)); sheetTypeList.Add(typeof(EnemySkillSheet)); sheetTypeList.Add(typeof(RuneOptionSheet)); + sheetTypeList.Add(typeof(GameConfigSheet)); } if (containRankingSimulatorSheets) @@ -614,6 +616,7 @@ public static bool TryGetSheet(this IWorldState worldState, Address address, sheetTypeList.Add(typeof(WeeklyArenaRewardSheet)); sheetTypeList.Add(typeof(CostumeStatSheet)); sheetTypeList.Add(typeof(RuneOptionSheet)); + sheetTypeList.Add(typeof(GameConfigSheet)); } if (containValidateItemRequirementSheets) @@ -641,6 +644,7 @@ public static bool TryGetSheet(this IWorldState worldState, Address address, sheetTypeList.Add(typeof(RuneWeightSheet)); sheetTypeList.Add(typeof(RuneSheet)); sheetTypeList.Add(typeof(RuneOptionSheet)); + sheetTypeList.Add(typeof(GameConfigSheet)); } return GetSheets(worldState, sheetTypeList.Distinct().ToArray()); @@ -779,7 +783,8 @@ public static StageSimulatorSheets GetStageSimulatorSheets(this IWorldState worl GetSheet(worldState), GetSheet(worldState), GetSheet(worldState), - GetSheet(worldState) + GetSheet(worldState), + GetSheet(worldState) ); } @@ -812,7 +817,8 @@ public static RankingSimulatorSheets GetRankingSimulatorSheets(this IWorldState GetSheet(worldState), GetSheet(worldState), GetSheet(worldState), - GetSheet(worldState) + GetSheet(worldState), + GetSheet(worldState) ); } diff --git a/Lib9c/TableData/SimulatorSheets.cs b/Lib9c/TableData/SimulatorSheets.cs index b819a7578e..d28ac5c934 100644 --- a/Lib9c/TableData/SimulatorSheets.cs +++ b/Lib9c/TableData/SimulatorSheets.cs @@ -1,9 +1,9 @@ - namespace Nekoyume.TableData { public class SimulatorSheets : SimulatorSheetsV1 { public readonly RuneOptionSheet RuneOptionSheet; + public readonly GameConfigSheet GameConiConfigSheet; public SimulatorSheets( MaterialItemSheet materialItemSheet, @@ -15,7 +15,8 @@ public SimulatorSheets( CharacterSheet characterSheet, CharacterLevelSheet characterLevelSheet, EquipmentItemSetEffectSheet equipmentItemSetEffectSheet, - RuneOptionSheet runeOptionSheet + RuneOptionSheet runeOptionSheet, + GameConfigSheet gameConfigSheet ) : base( materialItemSheet, skillSheet, @@ -28,6 +29,7 @@ RuneOptionSheet runeOptionSheet equipmentItemSetEffectSheet) { RuneOptionSheet = runeOptionSheet; + GameConiConfigSheet = gameConfigSheet; } } @@ -50,7 +52,8 @@ public StageSimulatorSheets( StageSheet stageSheet, StageWaveSheet stageWaveSheet, EnemySkillSheet enemySkillSheet, - RuneOptionSheet runeOptionSheet + RuneOptionSheet runeOptionSheet, + GameConfigSheet gameConfigSheet ) : base( materialItemSheet, skillSheet, @@ -61,7 +64,8 @@ RuneOptionSheet runeOptionSheet characterSheet, characterLevelSheet, equipmentItemSetEffectSheet, - runeOptionSheet + runeOptionSheet, + gameConfigSheet ) { StageSheet = stageSheet; @@ -85,7 +89,8 @@ public RankingSimulatorSheets( CharacterLevelSheet characterLevelSheet, EquipmentItemSetEffectSheet equipmentItemSetEffectSheet, WeeklyArenaRewardSheet weeklyArenaRewardSheet, - RuneOptionSheet runeOptionSheet + RuneOptionSheet runeOptionSheet, + GameConfigSheet gameConfigSheet ) : base( materialItemSheet, skillSheet, @@ -96,7 +101,8 @@ RuneOptionSheet runeOptionSheet characterSheet, characterLevelSheet, equipmentItemSetEffectSheet, - runeOptionSheet + runeOptionSheet, + gameConfigSheet ) { WeeklyArenaRewardSheet = weeklyArenaRewardSheet; @@ -120,7 +126,8 @@ public ArenaSimulatorSheets( EquipmentItemSetEffectSheet equipmentItemSetEffectSheet, CostumeStatSheet costumeStatSheet, WeeklyArenaRewardSheet weeklyArenaRewardSheet, - RuneOptionSheet runeOptionSheet + RuneOptionSheet runeOptionSheet, + GameConfigSheet gameConfigSheet ) : base(materialItemSheet, skillSheet, skillBuffSheet, @@ -130,11 +137,12 @@ RuneOptionSheet runeOptionSheet characterSheet, characterLevelSheet, equipmentItemSetEffectSheet, - runeOptionSheet) + runeOptionSheet, + gameConfigSheet + ) { CostumeStatSheet = costumeStatSheet; WeeklyArenaRewardSheet = weeklyArenaRewardSheet; - } } @@ -161,7 +169,8 @@ public RaidSimulatorSheets( WorldBossBattleRewardSheet worldBossBattleRewardSheet, RuneWeightSheet runeWeightSheet, RuneSheet runeSheet, - RuneOptionSheet runeOptionSheet + RuneOptionSheet runeOptionSheet, + GameConfigSheet gameConfigSheet ) : base(materialItemSheet, skillSheet, skillBuffSheet, @@ -171,7 +180,9 @@ RuneOptionSheet runeOptionSheet characterSheet, characterLevelSheet, equipmentItemSetEffectSheet, - runeOptionSheet) + runeOptionSheet, + gameConfigSheet + ) { WorldBossCharacterSheet = worldBossCharacterSheet; WorldBossActionPatternSheet = worldBossActionPatternSheet; From 18f2977533cf48976fe8fa9d9df5530038dad949 Mon Sep 17 00:00:00 2001 From: hyeon Date: Tue, 12 Mar 2024 21:14:13 +0900 Subject: [PATCH 03/17] Limit max damage of ShatterStrike --- Lib9c/Battle/Simulator.cs | 51 ++++++++++++++++++++++++++++++++ Lib9c/Model/Skill/AttackSkill.cs | 7 +++++ 2 files changed, 58 insertions(+) diff --git a/Lib9c/Battle/Simulator.cs b/Lib9c/Battle/Simulator.cs index 76b6532a27..28d21e1763 100644 --- a/Lib9c/Battle/Simulator.cs +++ b/Lib9c/Battle/Simulator.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Bencodex.Types; using Libplanet.Action; using Nekoyume.Model; using Nekoyume.Model.BattleStatus; @@ -33,6 +34,8 @@ public abstract class Simulator : ISimulator public readonly List StatDebuffList; public readonly List ActionDebuffList; + public long ShatterStrikeMaxDamage { get; private set; } + protected const int MaxTurn = 200; public int TurnNumber; public int WaveNumber { get; protected set; } @@ -48,6 +51,18 @@ protected Simulator(IRandom random, LogEvent = logEvent; } + protected Simulator(IRandom random, + AvatarState avatarState, + List foods, + SimulatorSheets simulatorSheets, bool logEvent = true) + : this(random, new Player(avatarState, simulatorSheets), foods, simulatorSheets) + { + LogEvent = logEvent; + ShatterStrikeMaxDamage = + new GameConfigState((Text)simulatorSheets.GameConiConfigSheet.Serialize()) + .ShatterStrikeMaxDamage; + } + protected Simulator( IRandom random, Player player, @@ -84,6 +99,42 @@ SimulatorSheetsV1 simulatorSheets Player.ResetCurrentHP(); } + protected Simulator( + IRandom random, + Player player, + List foods, + SimulatorSheets simulatorSheets + ) + { + Random = random; + MaterialItemSheet = simulatorSheets.MaterialItemSheet; + SkillSheet = simulatorSheets.SkillSheet; + SkillBuffSheet = simulatorSheets.SkillBuffSheet; + StatBuffSheet = simulatorSheets.StatBuffSheet; + SkillActionBuffSheet = simulatorSheets.SkillActionBuffSheet; + ActionBuffSheet = simulatorSheets.ActionBuffSheet; + CharacterSheet = simulatorSheets.CharacterSheet; + CharacterLevelSheet = simulatorSheets.CharacterLevelSheet; + EquipmentItemSetEffectSheet = simulatorSheets.EquipmentItemSetEffectSheet; + var debuffSkillIdList = SkillSheet.Values + .Where(s => s.SkillType == SkillType.Debuff).Select(s => s.Id); + StatDebuffList = SkillBuffSheet.Values.Where( + bf => debuffSkillIdList.Contains(bf.SkillId)).Aggregate( + new List(), + (current, bf) => current.Concat(bf.BuffIds).ToList() + ); + ActionDebuffList = SkillActionBuffSheet.Values.Where( + bf => debuffSkillIdList.Contains(bf.SkillId)).Aggregate( + new List(), + (current, bf) => current.Concat(bf.BuffIds).ToList() + ); + Log = new BattleLog(); + player.Simulator = this; + Player = player; + Player.Use(foods); + Player.ResetCurrentHP(); + } + public static List SetReward( WeightedSelector itemSelector, int maxCount, diff --git a/Lib9c/Model/Skill/AttackSkill.cs b/Lib9c/Model/Skill/AttackSkill.cs index bc348e69f3..f2c66fbf66 100644 --- a/Lib9c/Model/Skill/AttackSkill.cs +++ b/Lib9c/Model/Skill/AttackSkill.cs @@ -92,6 +92,13 @@ SkillRow.SkillCategory is not SkillCategory.ShatterStrike && // double attack must be shown as critical attack isCritical |= SkillRow.SkillCategory is SkillCategory.DoubleAttack; + + // ShatterStrike has max damage limitation + if (SkillRow.SkillCategory is SkillCategory.ShatterStrike) + { + damage = Math.Clamp(damage, + 1, caster.Simulator.ShatterStrikeMaxDamage); + } } target.CurrentHP -= damage; From 2f26395b621fe03efe027f1c3d0259e2e484ea6c Mon Sep 17 00:00:00 2001 From: hyeon Date: Tue, 12 Mar 2024 21:19:35 +0900 Subject: [PATCH 04/17] Limit max damage of ShatterStrike in arena --- Lib9c/Action/BattleArena.cs | 3 ++- Lib9c/Arena/ArenaSimulator.cs | 8 +++++++- Lib9c/Model/Skill/Arena/ArenaAttackSkill.cs | 8 ++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Lib9c/Action/BattleArena.cs b/Lib9c/Action/BattleArena.cs index 407d99f5d5..7a81ab4af6 100644 --- a/Lib9c/Action/BattleArena.cs +++ b/Lib9c/Action/BattleArena.cs @@ -404,7 +404,8 @@ public override IWorld Execute(IActionContext context) } for (var i = 0; i < ticket; i++) { - var simulator = new ArenaSimulator(random, HpIncreasingModifier); + var simulator = new ArenaSimulator(random, HpIncreasingModifier, + gameConfigState.ShatterStrikeMaxDamage); var log = simulator.Simulate( myArenaPlayerDigest, enemyArenaPlayerDigest, diff --git a/Lib9c/Arena/ArenaSimulator.cs b/Lib9c/Arena/ArenaSimulator.cs index d2dd618869..b7b26e11fa 100644 --- a/Lib9c/Arena/ArenaSimulator.cs +++ b/Lib9c/Arena/ArenaSimulator.cs @@ -22,11 +22,17 @@ public class ArenaSimulator : IArenaSimulator public ArenaLog Log { get; private set; } public int HpModifier { get; } - public ArenaSimulator(IRandom random, int hpModifier = 2) + public long ShatterStrikeMaxDamage { get; private set; } + + public ArenaSimulator(IRandom random, + int hpModifier = 2, + long shatterStrikeMaxDamage = 400_000 // 400K is initial limit of ShatterStrike. Use this as default. + ) { Random = random; Turn = 1; HpModifier = hpModifier; + ShatterStrikeMaxDamage = shatterStrikeMaxDamage; } public ArenaLog Simulate( diff --git a/Lib9c/Model/Skill/Arena/ArenaAttackSkill.cs b/Lib9c/Model/Skill/Arena/ArenaAttackSkill.cs index 6025242684..67489e291a 100644 --- a/Lib9c/Model/Skill/Arena/ArenaAttackSkill.cs +++ b/Lib9c/Model/Skill/Arena/ArenaAttackSkill.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Nekoyume.Arena; using Nekoyume.Battle; using Nekoyume.Model.Elemental; using Nekoyume.Model.Stat; @@ -64,6 +65,13 @@ protected ArenaAttackSkill( // double attack must be shown as critical attack isCritical |= SkillRow.SkillCategory == SkillCategory.DoubleAttack; + + // ShatterStrike has max damage limitation + if (SkillRow.SkillCategory is SkillCategory.ShatterStrike) + { + damage = Math.Clamp(damage, + 1, ((ArenaSimulator)caster.Simulator).ShatterStrikeMaxDamage); + } } infos.Add(new BattleStatus.Arena.ArenaSkill.ArenaSkillInfo( From 1fad883e87fb6c50ae928a3feabe04ce3a95c844 Mon Sep 17 00:00:00 2001 From: hyeon Date: Tue, 12 Mar 2024 22:16:05 +0900 Subject: [PATCH 05/17] Add tests for max damage of ShatterStrike --- .../Skill/Arena/ArenaShatterStrikeTest.cs | 27 +++++++------ .Lib9c.Tests/Model/Skill/ShatterStrikeTest.cs | 39 ++++++++++++------- Lib9c/Model/Skill/Arena/ArenaAttackSkill.cs | 9 +++-- 3 files changed, 46 insertions(+), 29 deletions(-) diff --git a/.Lib9c.Tests/Model/Skill/Arena/ArenaShatterStrikeTest.cs b/.Lib9c.Tests/Model/Skill/Arena/ArenaShatterStrikeTest.cs index 4aaec7502b..fcb3dbd68d 100644 --- a/.Lib9c.Tests/Model/Skill/Arena/ArenaShatterStrikeTest.cs +++ b/.Lib9c.Tests/Model/Skill/Arena/ArenaShatterStrikeTest.cs @@ -1,7 +1,9 @@ namespace Lib9c.Tests.Model.Skill.Arena { + using System; using System.Collections.Generic; using System.Linq; + using Bencodex.Types; using Lib9c.Tests.Action; using Nekoyume.Arena; using Nekoyume.Model; @@ -46,13 +48,15 @@ public ArenaShatterStrikeTest() [Theory] // 1bp == 0.01% - [InlineData(10000)] - [InlineData(1000)] - [InlineData(3700)] - [InlineData(100000)] - public void Use(int ratioBp) + [InlineData(2, 1000, false)] + [InlineData(2, 3700, false)] + [InlineData(2, 100_000, true)] + [InlineData(100_000, 100_000, false)] + public void Use(int hpModifier, int ratioBp, bool expectedEnemyDead) { - var simulator = new ArenaSimulator(new TestRandom()); + var gameConfigState = + new GameConfigState((Text)_tableSheets.GameConfigSheet.Serialize()); + var simulator = new ArenaSimulator(new TestRandom(), hpModifier: hpModifier); var myDigest = new ArenaPlayerDigest(_avatar1, _arenaAvatar1); var enemyDigest = new ArenaPlayerDigest(_avatar2, _arenaAvatar2); var arenaSheets = _tableSheets.GetArenaSimulatorSheets(); @@ -78,13 +82,14 @@ public void Use(int ratioBp) var used = shatterStrike.Use(challenger, enemy, simulator.Turn, new List()); Assert.Single(used.SkillInfos); Assert.Equal( - (long)(enemy.HP * ratioBp / 10000m) - enemy.DEF + challenger.ArmorPenetration, + Math.Clamp( + enemy.HP * ratioBp / 10000m - enemy.DEF + challenger.ArmorPenetration, + 1, + gameConfigState.ShatterStrikeMaxDamage + ), used.SkillInfos.First().Effect ); - if (ratioBp > 10000) - { - Assert.True(enemy.IsDead); - } + Assert.Equal(expectedEnemyDead, enemy.IsDead); } } } diff --git a/.Lib9c.Tests/Model/Skill/ShatterStrikeTest.cs b/.Lib9c.Tests/Model/Skill/ShatterStrikeTest.cs index 190ce2540c..13610d7b45 100644 --- a/.Lib9c.Tests/Model/Skill/ShatterStrikeTest.cs +++ b/.Lib9c.Tests/Model/Skill/ShatterStrikeTest.cs @@ -3,6 +3,7 @@ namespace Lib9c.Tests.Model.Skill using System; using System.Collections.Generic; using System.Linq; + using Bencodex.Types; using Lib9c.Tests.Action; using Libplanet.Crypto; using Nekoyume.Battle; @@ -19,16 +20,20 @@ public class ShatterStrikeTest [Theory] // 1bp == 0.01% - [InlineData(10000, true)] - [InlineData(10000, false)] - [InlineData(1000, true)] - [InlineData(1000, false)] - [InlineData(3700, true)] - [InlineData(3700, false)] - [InlineData(100000, true)] - [InlineData(100000, false)] - public void Use(int ratioBp, bool copyCharacter) + [InlineData(100, 10000, false, true)] + [InlineData(100, 10000, false, false)] + [InlineData(100, 1000, false, true)] + [InlineData(100, 1000, false, false)] + [InlineData(100, 3700, false, true)] + [InlineData(100, 3700, false, false)] + [InlineData(100, 100_000, true, true)] + [InlineData(100, 100_000, true, false)] + [InlineData(1_000_000, 100_000, false, true)] + [InlineData(1_000_000, 100_000, false, false)] + public void Use(int enemyHp, int ratioBp, bool expectedEnemyDead, bool copyCharacter) { + var gameConfigState = + new GameConfigState((Text)_tableSheets.GameConfigSheet.Serialize()); Assert.True( _tableSheets.SkillSheet.TryGetValue(700011, out var skillRow) ); // 700011 is ShatterStrike @@ -71,6 +76,11 @@ public void Use(int ratioBp, bool copyCharacter) var player = new Player(avatarState, simulator); var enemyRow = _tableSheets.CharacterSheet.OrderedList .FirstOrDefault(e => e.Id > 200000); + enemyRow.Set(new[] + { + "201000", "XS", "2", enemyHp.ToString(), "16", "6", "4", "90", "15", "3.2", "0.64", + "0.24", "0", "3.6", "0.6", "0.8", "1.2", + }); Assert.NotNull(enemyRow); var enemy = new Enemy(player, enemyRow, 1); @@ -80,13 +90,14 @@ public void Use(int ratioBp, bool copyCharacter) Assert.NotNull(used); var skillInfo = Assert.Single(used.SkillInfos); Assert.Equal( - (long)(enemy.HP * ratioBp / 10000m) - enemy.DEF + player.ArmorPenetration, + Math.Clamp( + enemy.HP * ratioBp / 10000m - enemy.DEF + player.ArmorPenetration, + 1, + gameConfigState.ShatterStrikeMaxDamage + ), skillInfo.Effect ); - if (ratioBp > 10000) - { - Assert.True(skillInfo.IsDead); - } + Assert.Equal(expectedEnemyDead, skillInfo.IsDead); } } } diff --git a/Lib9c/Model/Skill/Arena/ArenaAttackSkill.cs b/Lib9c/Model/Skill/Arena/ArenaAttackSkill.cs index 67489e291a..9899999c87 100644 --- a/Lib9c/Model/Skill/Arena/ArenaAttackSkill.cs +++ b/Lib9c/Model/Skill/Arena/ArenaAttackSkill.cs @@ -61,10 +61,6 @@ protected ArenaAttackSkill( damage = Math.Max(damage - finalDEF, 1); // Apply damage reduce damage = (int)((damage - target.DRV) * (1 - target.DRR / 10000m)); - target.CurrentHP -= damage; - - // double attack must be shown as critical attack - isCritical |= SkillRow.SkillCategory == SkillCategory.DoubleAttack; // ShatterStrike has max damage limitation if (SkillRow.SkillCategory is SkillCategory.ShatterStrike) @@ -72,6 +68,11 @@ protected ArenaAttackSkill( damage = Math.Clamp(damage, 1, ((ArenaSimulator)caster.Simulator).ShatterStrikeMaxDamage); } + + target.CurrentHP -= damage; + + // double attack must be shown as critical attack + isCritical |= SkillRow.SkillCategory == SkillCategory.DoubleAttack; } infos.Add(new BattleStatus.Arena.ArenaSkill.ArenaSkillInfo( From 88173549b7dba8238ff07aaa7d2812bb4084adcc Mon Sep 17 00:00:00 2001 From: hyeon Date: Tue, 12 Mar 2024 22:21:35 +0900 Subject: [PATCH 06/17] Remove unused code for Dispel --- Lib9c/Battle/Simulator.cs | 27 ------------------------- Lib9c/Model/Character/ArenaCharacter.cs | 20 ------------------ 2 files changed, 47 deletions(-) diff --git a/Lib9c/Battle/Simulator.cs b/Lib9c/Battle/Simulator.cs index 28d21e1763..964e303c05 100644 --- a/Lib9c/Battle/Simulator.cs +++ b/Lib9c/Battle/Simulator.cs @@ -31,9 +31,6 @@ public abstract class Simulator : ISimulator public readonly CharacterLevelSheet CharacterLevelSheet; public readonly EquipmentItemSetEffectSheet EquipmentItemSetEffectSheet; - public readonly List StatDebuffList; - public readonly List ActionDebuffList; - public long ShatterStrikeMaxDamage { get; private set; } protected const int MaxTurn = 200; @@ -80,18 +77,6 @@ SimulatorSheetsV1 simulatorSheets CharacterSheet = simulatorSheets.CharacterSheet; CharacterLevelSheet = simulatorSheets.CharacterLevelSheet; EquipmentItemSetEffectSheet = simulatorSheets.EquipmentItemSetEffectSheet; - var debuffSkillIdList = SkillSheet.Values - .Where(s => s.SkillType == SkillType.Debuff).Select(s => s.Id); - StatDebuffList = SkillBuffSheet.Values.Where( - bf => debuffSkillIdList.Contains(bf.SkillId)).Aggregate( - new List(), - (current, bf) => current.Concat(bf.BuffIds).ToList() - ); - ActionDebuffList = SkillActionBuffSheet.Values.Where( - bf => debuffSkillIdList.Contains(bf.SkillId)).Aggregate( - new List(), - (current, bf) => current.Concat(bf.BuffIds).ToList() - ); Log = new BattleLog(); player.Simulator = this; Player = player; @@ -116,18 +101,6 @@ SimulatorSheets simulatorSheets CharacterSheet = simulatorSheets.CharacterSheet; CharacterLevelSheet = simulatorSheets.CharacterLevelSheet; EquipmentItemSetEffectSheet = simulatorSheets.EquipmentItemSetEffectSheet; - var debuffSkillIdList = SkillSheet.Values - .Where(s => s.SkillType == SkillType.Debuff).Select(s => s.Id); - StatDebuffList = SkillBuffSheet.Values.Where( - bf => debuffSkillIdList.Contains(bf.SkillId)).Aggregate( - new List(), - (current, bf) => current.Concat(bf.BuffIds).ToList() - ); - ActionDebuffList = SkillActionBuffSheet.Values.Where( - bf => debuffSkillIdList.Contains(bf.SkillId)).Aggregate( - new List(), - (current, bf) => current.Concat(bf.BuffIds).ToList() - ); Log = new BattleLog(); player.Simulator = this; Player = player; diff --git a/Lib9c/Model/Character/ArenaCharacter.cs b/Lib9c/Model/Character/ArenaCharacter.cs index 3230e94bc5..1cb146a7d8 100644 --- a/Lib9c/Model/Character/ArenaCharacter.cs +++ b/Lib9c/Model/Character/ArenaCharacter.cs @@ -30,9 +30,6 @@ public class ArenaCharacter : ICloneable private readonly ActionBuffSheet _actionBuffSheet; private readonly ArenaSkills _skills; - private readonly List _statDebuffList; - private readonly List _actionDebuffList; - public readonly IArenaSimulator Simulator; public readonly ArenaSkills _runeSkills = new ArenaSkills(); public readonly Dictionary RuneSkillCooldownMap = new Dictionary(); @@ -144,20 +141,6 @@ public ArenaCharacter( _skillActionBuffSheet = sheets.SkillActionBuffSheet; _actionBuffSheet = sheets.ActionBuffSheet; - var debuffSkillIdList = _skillSheet.Values - .Where(s => s.SkillType == SkillType.Debuff).Select(s => s.Id); - _statDebuffList = _skillBuffSheet.Values.Where( - bf => debuffSkillIdList.Contains(bf.SkillId)).Aggregate( - new List(), - (current, bf) => current.Concat(bf.BuffIds).ToList() - ); - _actionDebuffList = _skillActionBuffSheet.Values.Where( - bf => debuffSkillIdList.Contains(bf.SkillId)).Aggregate( - new List(), - (current, bf) => current.Concat(bf.BuffIds).ToList() - ); - - Simulator = simulator; Stats = GetStatV1( digest, @@ -238,9 +221,6 @@ private ArenaCharacter(ArenaCharacter value) _skillActionBuffSheet = value._skillActionBuffSheet; _actionBuffSheet = value._actionBuffSheet; - _statDebuffList = value._statDebuffList; - _actionDebuffList = value._actionDebuffList; - Simulator = value.Simulator; Stats = new CharacterStats(value.Stats); _skills = value._skills; From 8911640c15b017e90df124d8f7cf8698c1d568f4 Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Tue, 12 Mar 2024 09:32:41 +0900 Subject: [PATCH 07/17] Removed HackAndSlash3 --- Lib9c/Action/HackAndSlash3.cs | 294 ---------------------------------- 1 file changed, 294 deletions(-) delete mode 100644 Lib9c/Action/HackAndSlash3.cs diff --git a/Lib9c/Action/HackAndSlash3.cs b/Lib9c/Action/HackAndSlash3.cs deleted file mode 100644 index bf122cd283..0000000000 --- a/Lib9c/Action/HackAndSlash3.cs +++ /dev/null @@ -1,294 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Bencodex.Types; -using Lib9c.Abstractions; -using Libplanet.Action; -using Libplanet.Action.State; -using Libplanet.Crypto; -using Nekoyume.Battle; -using Nekoyume.Model.BattleStatus; -using Nekoyume.Model.State; -using Nekoyume.Module; -using Nekoyume.TableData; -using Serilog; - -namespace Nekoyume.Action -{ - [Serializable] - [ActionObsolete(ActionObsoleteConfig.V200020AccidentObsoleteIndex)] - [ActionType("hack_and_slash3")] - public class HackAndSlash3 : GameAction, IHackAndSlashV1 - { - public List costumes; - public List equipments; - public List foods; - public int worldId; - public int stageId; - public Address avatarAddress; - public Address WeeklyArenaAddress; - public Address RankingMapAddress; - public BattleLog Result { get; private set; } - - IEnumerable IHackAndSlashV1.Costumes => costumes; - IEnumerable IHackAndSlashV1.Equipments => equipments; - IEnumerable IHackAndSlashV1.Foods => foods; - int IHackAndSlashV1.WorldId => worldId; - int IHackAndSlashV1.StageId => stageId; - Address IHackAndSlashV1.AvatarAddress => avatarAddress; - Address IHackAndSlashV1.WeeklyArenaAddress => WeeklyArenaAddress; - Address IHackAndSlashV1.RankingMapAddress => RankingMapAddress; - - protected override IImmutableDictionary PlainValueInternal => - new Dictionary - { - ["costumes"] = new List(costumes.OrderBy(i => i).Select(e => e.Serialize())), - ["equipments"] = new List(equipments.OrderBy(i => i).Select(e => e.Serialize())), - ["foods"] = new List(foods.OrderBy(i => i).Select(e => e.Serialize())), - ["worldId"] = worldId.Serialize(), - ["stageId"] = stageId.Serialize(), - ["avatarAddress"] = avatarAddress.Serialize(), - ["weeklyArenaAddress"] = WeeklyArenaAddress.Serialize(), - ["rankingMapAddress"] = RankingMapAddress.Serialize(), - }.ToImmutableDictionary(); - - - protected override void LoadPlainValueInternal( - IImmutableDictionary plainValue) - { - costumes = ((List) plainValue["costumes"]).Select(e => e.ToInteger()).ToList(); - equipments = ((List) plainValue["equipments"]).Select(e => e.ToGuid()).ToList(); - foods = ((List) plainValue["foods"]).Select(e => e.ToGuid()).ToList(); - worldId = plainValue["worldId"].ToInteger(); - stageId = plainValue["stageId"].ToInteger(); - avatarAddress = plainValue["avatarAddress"].ToAddress(); - WeeklyArenaAddress = plainValue["weeklyArenaAddress"].ToAddress(); - RankingMapAddress = plainValue["rankingMapAddress"].ToAddress(); - } - - public override IWorld Execute(IActionContext context) - { - context.UseGas(1); - IActionContext ctx = context; - var states = ctx.PreviousState; - - CheckObsolete(ActionObsoleteConfig.V100080ObsoleteIndex, context); - - var addressesHex = GetSignerAndOtherAddressesHex(context, avatarAddress); - - var sw = new Stopwatch(); - sw.Start(); - var started = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}HAS exec started", addressesHex); - - if (!states.TryGetAvatarState(ctx.Signer, avatarAddress, out AvatarState avatarState)) - { - throw new FailedLoadStateException($"{addressesHex}Aborted as the avatar state of the signer was failed to load."); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Get AgentAvatarStates: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - - if (avatarState.RankingMapAddress != RankingMapAddress) - { - throw new InvalidAddressException($"{addressesHex}Invalid ranking map address"); - } - - // worldId와 stageId가 유효한지 확인합니다. - var worldSheet = states.GetSheet(); - - if (!worldSheet.TryGetValue(worldId, out var worldRow, false)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(WorldSheet), worldId); - } - - if (stageId < worldRow.StageBegin || - stageId > worldRow.StageEnd) - { - throw new SheetRowColumnException( - $"{addressesHex}{worldId} world is not contains {worldRow.Id} stage: " + - $"{worldRow.StageBegin}-{worldRow.StageEnd}"); - } - - var stageSheet = states.GetSheet(); - if (!stageSheet.TryGetValue(stageId, out var stageRow)) - { - throw new SheetRowNotFoundException(addressesHex, nameof(StageSheet), stageId); - } - - var worldInformation = avatarState.worldInformation; - if (!worldInformation.TryGetWorld(worldId, out var world)) - { - // NOTE: Add new World from WorldSheet - worldInformation.AddAndUnlockNewWorld(worldRow, ctx.BlockIndex, worldSheet); - } - - if (!world.IsUnlocked) - { - throw new InvalidWorldException($"{addressesHex}{worldId} is locked."); - } - - if (world.StageBegin != worldRow.StageBegin || - world.StageEnd != worldRow.StageEnd) - { - worldInformation.UpdateWorld(worldRow); - } - - if (world.IsStageCleared && stageId > world.StageClearedId + 1 || - !world.IsStageCleared && stageId != world.StageBegin) - { - throw new InvalidStageException( - $"{addressesHex}Aborted as the stage ({worldId}/{stageId}) is not cleared; " + - $"cleared stage: {world.StageClearedId}" - ); - } - - avatarState.ValidateEquipments(equipments, context.BlockIndex); - avatarState.ValidateConsumable(foods, context.BlockIndex); - avatarState.ValidateCostume(new HashSet(costumes)); - - var costumeStatSheet = states.GetSheet(); - - sw.Restart(); - if (avatarState.actionPoint < stageRow.CostAP) - { - throw new NotEnoughActionPointException( - $"{addressesHex}Aborted due to insufficient action point: " + - $"{avatarState.actionPoint} < {stageRow.CostAP}" - ); - } - - avatarState.actionPoint -= stageRow.CostAP; - - avatarState.EquipCostumes(new HashSet(costumes)); - - avatarState.EquipEquipments(equipments); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Unequip items: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var characterSheet = states.GetSheet(); - var random = ctx.GetRandom(); - var simulator = new StageSimulatorV1( - random, - avatarState, - foods, - worldId, - stageId, - states.GetStageSimulatorSheetsV1(), - costumeStatSheet - ); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - simulator.SimulateV1(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Simulator.Simulate(): {Elapsed}", addressesHex, sw.Elapsed); - - Log.Verbose( - "{AddressesHex}Execute HackAndSlash({AvatarAddress}); worldId: {WorldId}, stageId: {StageId}, result: {Result}, " + - "clearWave: {ClearWave}, totalWave: {TotalWave}", - addressesHex, - avatarAddress, - worldId, - stageId, - simulator.Log.result, - simulator.Log.clearedWaveNumber, - simulator.Log.waveCount - ); - - sw.Restart(); - if (simulator.Log.IsClear) - { - var worldUnlockSheet = states.GetSheet(); - simulator.Player.worldInformation.ClearStage( - worldId, - stageId, - ctx.BlockIndex, - worldSheet, - worldUnlockSheet - ); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS ClearStage: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - avatarState.Update(simulator); - - var materialSheet = states.GetSheet(); - avatarState.UpdateQuestRewards2(materialSheet); - - avatarState.updatedAt = ctx.BlockIndex; - avatarState.mailBox.CleanUpV1(); - states = states.SetAvatarState(avatarAddress, avatarState); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Set AvatarState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - if (states.TryGetLegacyState(RankingMapAddress, out Dictionary d) && simulator.Log.IsClear) - { - var ranking = new RankingMapState(d); - ranking.Update(avatarState); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update RankingState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - - var serialized = ranking.Serialize(); - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Serialize RankingState: {Elapsed}", addressesHex, sw.Elapsed); - sw.Restart(); - states = states.SetLegacyState(RankingMapAddress, serialized); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Set RankingState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - if (simulator.Log.stageId >= GameConfig.RequireClearedStageLevel.ActionsInRankingBoard && - simulator.Log.IsClear && - states.TryGetLegacyState(WeeklyArenaAddress, out Dictionary weeklyDict)) - { - var weekly = new WeeklyArenaState(weeklyDict); - if (!weekly.Ended) - { - if (weekly.ContainsKey(avatarAddress)) - { - var info = weekly[avatarAddress]; - info.UpdateV2(avatarState, characterSheet, costumeStatSheet); - weekly.Update(info); - } - else - { - weekly.SetV2(avatarState, characterSheet, costumeStatSheet); - } - - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Update WeeklyArenaState: {Elapsed}", addressesHex, sw.Elapsed); - - sw.Restart(); - var weeklySerialized = weekly.Serialize(); - sw.Stop(); - Log.Verbose("{AddressesHex}HAS Serialize RankingState: {Elapsed}", addressesHex, sw.Elapsed); - - states = states.SetLegacyState(weekly.address, weeklySerialized); - } - } - - Result = simulator.Log; - - var ended = DateTimeOffset.UtcNow; - Log.Verbose("{AddressesHex}HAS Total Executed Time: {Elapsed}", addressesHex, ended - started); - return states; - } - } -} From c21766a4b6e8b610c4da16519d7a4d64fd7b4b8d Mon Sep 17 00:00:00 2001 From: Say Cheong Date: Wed, 13 Mar 2024 15:54:52 +0900 Subject: [PATCH 08/17] Build fix --- .../Scenario/WorldUnlockScenarioTest.cs | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/.Lib9c.Tests/Action/Scenario/WorldUnlockScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/WorldUnlockScenarioTest.cs index 5ef3fcdb83..c158ccce4c 100644 --- a/.Lib9c.Tests/Action/Scenario/WorldUnlockScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/WorldUnlockScenarioTest.cs @@ -3,9 +3,9 @@ namespace Lib9c.Tests.Action.Scenario using System; using System.Collections.Generic; using System.Linq; - using System.Text; using Libplanet.Action.State; using Libplanet.Crypto; + using Libplanet.Types.Assets; using Nekoyume; using Nekoyume.Action; using Nekoyume.Model; @@ -35,12 +35,13 @@ public WorldUnlockScenarioTest() _avatarAddress = _agentAddress.Derive("avatar"); _rankingMapAddress = _avatarAddress.Derive("ranking_map"); + var gameConfigState = new GameConfigState(sheets[nameof(GameConfigSheet)]); var avatarState = new AvatarState( _avatarAddress, _agentAddress, 0, _tableSheets.GetAvatarSheets(), - new GameConfigState(sheets[nameof(GameConfigSheet)]), + gameConfigState, _rankingMapAddress ) { @@ -50,11 +51,18 @@ public WorldUnlockScenarioTest() _weeklyArenaState = new WeeklyArenaState(0); +#pragma warning disable CS0618 + // Use of obsolete method Currency.Legacy(): https://github.com/planetarium/lib9c/discussions/1319 + var currency = Currency.Legacy("NCG", 2, null); +#pragma warning restore CS0618 + var goldCurrencyState = new GoldCurrencyState(currency); _initialState = new World(new MockWorldState()) + .SetLegacyState(Addresses.GoldCurrency, goldCurrencyState.Serialize()) .SetLegacyState(_weeklyArenaState.address, _weeklyArenaState.Serialize()) .SetAgentState(_agentAddress, agentState) .SetAvatarState(_avatarAddress, avatarState) - .SetLegacyState(_rankingMapAddress, new RankingMapState(_rankingMapAddress).Serialize()); + .SetLegacyState(_rankingMapAddress, new RankingMapState(_rankingMapAddress).Serialize()) + .SetLegacyState(gameConfigState.address, gameConfigState.Serialize()); foreach (var (key, value) in sheets) { @@ -90,17 +98,20 @@ public void UnlockWorldByHackAndSlashAfterPatchTableWithAddRow( var doomfist = Doomfist.GetOne(_tableSheets, avatarState.level, ItemSubType.Weapon); avatarState.inventory.AddItem(doomfist); - var nextState = _initialState.SetAvatarState(_avatarAddress, avatarState); - var hackAndSlash = new HackAndSlash3 + var nextState = _initialState + .SetAvatarState(_avatarAddress, avatarState) + .SetLegacyState( + _avatarAddress.Derive("world_ids"), + new Bencodex.Types.List(Enumerable.Range(1, worldIdToClear).Select(i => i.Serialize()))); + var hackAndSlash = new HackAndSlash { - worldId = worldIdToClear, - stageId = stageIdToClear, - avatarAddress = _avatarAddress, - costumes = new List(), - equipments = new List { doomfist.NonFungibleId }, - foods = new List(), - WeeklyArenaAddress = _weeklyArenaState.address, - RankingMapAddress = _rankingMapAddress, + WorldId = worldIdToClear, + StageId = stageIdToClear, + AvatarAddress = _avatarAddress, + Costumes = new List(), + Equipments = new List { doomfist.NonFungibleId }, + Foods = new List(), + RuneInfos = new List(), }; nextState = hackAndSlash.Execute(new ActionContext { @@ -108,7 +119,6 @@ public void UnlockWorldByHackAndSlashAfterPatchTableWithAddRow( Signer = _agentAddress, RandomSeed = 0, }); - Assert.True(hackAndSlash.Result.IsClear); avatarState = nextState.GetAvatarState(_avatarAddress); Assert.True(avatarState.worldInformation.IsStageCleared(stageIdToClear)); @@ -149,7 +159,6 @@ public void UnlockWorldByHackAndSlashAfterPatchTableWithAddRow( Signer = _agentAddress, RandomSeed = 0, }); - Assert.True(hackAndSlash.Result.IsClear); avatarState = nextState.GetAvatarState(_avatarAddress); Assert.True(avatarState.worldInformation.IsWorldUnlocked(worldIdToUnlock)); From ac6a259fd9d565e7a543facd5d16d5e55ac24550 Mon Sep 17 00:00:00 2001 From: sonohoshi Date: Wed, 13 Mar 2024 17:31:55 +0900 Subject: [PATCH 09/17] remove checking stageCleared for ClearStage invoking --- Lib9c/Action/HackAndSlash.cs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/Lib9c/Action/HackAndSlash.cs b/Lib9c/Action/HackAndSlash.cs index a2b05c2592..e1ef713d78 100644 --- a/Lib9c/Action/HackAndSlash.cs +++ b/Lib9c/Action/HackAndSlash.cs @@ -508,17 +508,14 @@ public IWorld Execute( sw.Restart(); if (simulator.Log.IsClear) { - if (!stageCleared) - { - avatarState.worldInformation.ClearStage( - WorldId, - StageId, - blockIndex, - worldSheet, - worldUnlockSheet - ); - stageCleared = true; - } + avatarState.worldInformation.ClearStage( + WorldId, + StageId, + blockIndex, + worldSheet, + worldUnlockSheet + ); + stageCleared = true; sw.Stop(); Log.Verbose("{AddressesHex} {Source} HAS {Process} from #{BlockIndex}: {Elapsed}", addressesHex, source, "ClearStage", blockIndex, sw.Elapsed.TotalMilliseconds); From 94874f784eb08dd7769f7971f228aea247218728 Mon Sep 17 00:00:00 2001 From: hyeon Date: Wed, 13 Mar 2024 15:03:58 +0900 Subject: [PATCH 10/17] Revert "Add GameConfigSheet to simulators": Pass value This reverts commit f5bf263856b5b8714272d3e55ebf8be18103a689. --- .Lib9c.Tests/TableSheets.cs | 15 +++++-------- Lib9c/Extensions/SheetsExtensions.cs | 15 +++++-------- Lib9c/Module/LegacyModule.cs | 10 ++------- Lib9c/TableData/SimulatorSheets.cs | 33 ++++++++++------------------ 4 files changed, 23 insertions(+), 50 deletions(-) diff --git a/.Lib9c.Tests/TableSheets.cs b/.Lib9c.Tests/TableSheets.cs index cbbadbe683..67d5497355 100644 --- a/.Lib9c.Tests/TableSheets.cs +++ b/.Lib9c.Tests/TableSheets.cs @@ -285,8 +285,7 @@ public SimulatorSheets GetSimulatorSheets() CharacterSheet, CharacterLevelSheet, EquipmentItemSetEffectSheet, - RuneOptionSheet, - GameConfigSheet + RuneOptionSheet ); } @@ -320,8 +319,7 @@ public StageSimulatorSheets GetStageSimulatorSheets() StageSheet, StageWaveSheet, EnemySkillSheet, - RuneOptionSheet, - GameConfigSheet + RuneOptionSheet ); } @@ -372,8 +370,7 @@ public RankingSimulatorSheets GetRankingSimulatorSheets() CharacterLevelSheet, EquipmentItemSetEffectSheet, WeeklyArenaRewardSheet, - RuneOptionSheet, - GameConfigSheet + RuneOptionSheet ); } @@ -408,8 +405,7 @@ public ArenaSimulatorSheets GetArenaSimulatorSheets() EquipmentItemSetEffectSheet, CostumeStatSheet, WeeklyArenaRewardSheet, - RuneOptionSheet, - GameConfigSheet + RuneOptionSheet ); } @@ -450,8 +446,7 @@ public RaidSimulatorSheets GetRaidSimulatorSheets() WorldBossBattleRewardSheet, RuneWeightSheet, RuneSheet, - RuneOptionSheet, - GameConfigSheet + RuneOptionSheet ); } diff --git a/Lib9c/Extensions/SheetsExtensions.cs b/Lib9c/Extensions/SheetsExtensions.cs index b604e933ae..27bf6b069f 100644 --- a/Lib9c/Extensions/SheetsExtensions.cs +++ b/Lib9c/Extensions/SheetsExtensions.cs @@ -191,8 +191,7 @@ public static SimulatorSheets GetSimulatorSheets( sheets.GetSheet(), sheets.GetSheet(), sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet() + sheets.GetSheet() ); } @@ -245,8 +244,7 @@ public static StageSimulatorSheets GetStageSimulatorSheets( sheets.GetSheet(), sheets.GetSheet(), sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet() + sheets.GetSheet() ); } @@ -298,8 +296,7 @@ public static RankingSimulatorSheets GetRankingSimulatorSheets( sheets.GetSheet(), sheets.GetSheet(), sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet() + sheets.GetSheet() ); } @@ -351,8 +348,7 @@ public static ArenaSimulatorSheets GetArenaSimulatorSheets( sheets.GetSheet(), sheets.GetSheet(), sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet() + sheets.GetSheet() ); } @@ -411,8 +407,7 @@ public static RaidSimulatorSheets GetRaidSimulatorSheets( sheets.GetSheet(), sheets.GetSheet(), sheets.GetSheet(), - sheets.GetSheet(), - sheets.GetSheet() + sheets.GetSheet() ); } diff --git a/Lib9c/Module/LegacyModule.cs b/Lib9c/Module/LegacyModule.cs index f4fa1317a9..d4313b3577 100644 --- a/Lib9c/Module/LegacyModule.cs +++ b/Lib9c/Module/LegacyModule.cs @@ -566,7 +566,6 @@ public static bool TryGetSheet(this IWorldState worldState, Address address, sheetTypeList.Add(typeof(CharacterLevelSheet)); sheetTypeList.Add(typeof(EquipmentItemSetEffectSheet)); sheetTypeList.Add(typeof(RuneOptionSheet)); - sheetTypeList.Add(typeof(GameConfigSheet)); } if (containStageSimulatorSheets) @@ -584,7 +583,6 @@ public static bool TryGetSheet(this IWorldState worldState, Address address, sheetTypeList.Add(typeof(StageWaveSheet)); sheetTypeList.Add(typeof(EnemySkillSheet)); sheetTypeList.Add(typeof(RuneOptionSheet)); - sheetTypeList.Add(typeof(GameConfigSheet)); } if (containRankingSimulatorSheets) @@ -616,7 +614,6 @@ public static bool TryGetSheet(this IWorldState worldState, Address address, sheetTypeList.Add(typeof(WeeklyArenaRewardSheet)); sheetTypeList.Add(typeof(CostumeStatSheet)); sheetTypeList.Add(typeof(RuneOptionSheet)); - sheetTypeList.Add(typeof(GameConfigSheet)); } if (containValidateItemRequirementSheets) @@ -644,7 +641,6 @@ public static bool TryGetSheet(this IWorldState worldState, Address address, sheetTypeList.Add(typeof(RuneWeightSheet)); sheetTypeList.Add(typeof(RuneSheet)); sheetTypeList.Add(typeof(RuneOptionSheet)); - sheetTypeList.Add(typeof(GameConfigSheet)); } return GetSheets(worldState, sheetTypeList.Distinct().ToArray()); @@ -783,8 +779,7 @@ public static StageSimulatorSheets GetStageSimulatorSheets(this IWorldState worl GetSheet(worldState), GetSheet(worldState), GetSheet(worldState), - GetSheet(worldState), - GetSheet(worldState) + GetSheet(worldState) ); } @@ -817,8 +812,7 @@ public static RankingSimulatorSheets GetRankingSimulatorSheets(this IWorldState GetSheet(worldState), GetSheet(worldState), GetSheet(worldState), - GetSheet(worldState), - GetSheet(worldState) + GetSheet(worldState) ); } diff --git a/Lib9c/TableData/SimulatorSheets.cs b/Lib9c/TableData/SimulatorSheets.cs index d28ac5c934..b819a7578e 100644 --- a/Lib9c/TableData/SimulatorSheets.cs +++ b/Lib9c/TableData/SimulatorSheets.cs @@ -1,9 +1,9 @@ + namespace Nekoyume.TableData { public class SimulatorSheets : SimulatorSheetsV1 { public readonly RuneOptionSheet RuneOptionSheet; - public readonly GameConfigSheet GameConiConfigSheet; public SimulatorSheets( MaterialItemSheet materialItemSheet, @@ -15,8 +15,7 @@ public SimulatorSheets( CharacterSheet characterSheet, CharacterLevelSheet characterLevelSheet, EquipmentItemSetEffectSheet equipmentItemSetEffectSheet, - RuneOptionSheet runeOptionSheet, - GameConfigSheet gameConfigSheet + RuneOptionSheet runeOptionSheet ) : base( materialItemSheet, skillSheet, @@ -29,7 +28,6 @@ GameConfigSheet gameConfigSheet equipmentItemSetEffectSheet) { RuneOptionSheet = runeOptionSheet; - GameConiConfigSheet = gameConfigSheet; } } @@ -52,8 +50,7 @@ public StageSimulatorSheets( StageSheet stageSheet, StageWaveSheet stageWaveSheet, EnemySkillSheet enemySkillSheet, - RuneOptionSheet runeOptionSheet, - GameConfigSheet gameConfigSheet + RuneOptionSheet runeOptionSheet ) : base( materialItemSheet, skillSheet, @@ -64,8 +61,7 @@ GameConfigSheet gameConfigSheet characterSheet, characterLevelSheet, equipmentItemSetEffectSheet, - runeOptionSheet, - gameConfigSheet + runeOptionSheet ) { StageSheet = stageSheet; @@ -89,8 +85,7 @@ public RankingSimulatorSheets( CharacterLevelSheet characterLevelSheet, EquipmentItemSetEffectSheet equipmentItemSetEffectSheet, WeeklyArenaRewardSheet weeklyArenaRewardSheet, - RuneOptionSheet runeOptionSheet, - GameConfigSheet gameConfigSheet + RuneOptionSheet runeOptionSheet ) : base( materialItemSheet, skillSheet, @@ -101,8 +96,7 @@ GameConfigSheet gameConfigSheet characterSheet, characterLevelSheet, equipmentItemSetEffectSheet, - runeOptionSheet, - gameConfigSheet + runeOptionSheet ) { WeeklyArenaRewardSheet = weeklyArenaRewardSheet; @@ -126,8 +120,7 @@ public ArenaSimulatorSheets( EquipmentItemSetEffectSheet equipmentItemSetEffectSheet, CostumeStatSheet costumeStatSheet, WeeklyArenaRewardSheet weeklyArenaRewardSheet, - RuneOptionSheet runeOptionSheet, - GameConfigSheet gameConfigSheet + RuneOptionSheet runeOptionSheet ) : base(materialItemSheet, skillSheet, skillBuffSheet, @@ -137,12 +130,11 @@ GameConfigSheet gameConfigSheet characterSheet, characterLevelSheet, equipmentItemSetEffectSheet, - runeOptionSheet, - gameConfigSheet - ) + runeOptionSheet) { CostumeStatSheet = costumeStatSheet; WeeklyArenaRewardSheet = weeklyArenaRewardSheet; + } } @@ -169,8 +161,7 @@ public RaidSimulatorSheets( WorldBossBattleRewardSheet worldBossBattleRewardSheet, RuneWeightSheet runeWeightSheet, RuneSheet runeSheet, - RuneOptionSheet runeOptionSheet, - GameConfigSheet gameConfigSheet + RuneOptionSheet runeOptionSheet ) : base(materialItemSheet, skillSheet, skillBuffSheet, @@ -180,9 +171,7 @@ GameConfigSheet gameConfigSheet characterSheet, characterLevelSheet, equipmentItemSetEffectSheet, - runeOptionSheet, - gameConfigSheet - ) + runeOptionSheet) { WorldBossCharacterSheet = worldBossCharacterSheet; WorldBossActionPatternSheet = worldBossActionPatternSheet; From 6e21e917e351304fe11d2ab398db1162bfb60d56 Mon Sep 17 00:00:00 2001 From: hyeon Date: Wed, 13 Mar 2024 21:12:52 +0900 Subject: [PATCH 11/17] Add more Dispel tests - Check dispel not to affect for all actions --- .Lib9c.Tests/Model/Skill/CombatTest.cs | 57 ++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/.Lib9c.Tests/Model/Skill/CombatTest.cs b/.Lib9c.Tests/Model/Skill/CombatTest.cs index 652f03b4b2..baca0e460c 100644 --- a/.Lib9c.Tests/Model/Skill/CombatTest.cs +++ b/.Lib9c.Tests/Model/Skill/CombatTest.cs @@ -249,6 +249,63 @@ public void DispelOnDuration_Affect() Assert.True(battleStatus.SkillInfos.First().Affected); } + [Fact] + public void DispelOnDuration_Nothing() + { + const int actionBuffId = 708000; // Dispel with duration + var actionBuffSheet = _tableSheets.ActionBuffSheet; + + // Use Dispel first + var dispel = actionBuffSheet.Values.First(bf => bf.Id == actionBuffId); + _player.AddBuff(BuffFactory.GetActionBuff(_player.Stats, dispel)); + Assert.Single(_player.Buffs); + + // Add Bleed + var bleed = actionBuffSheet.Values.First(bf => bf.Id == 600001); + _player.AddBuff(BuffFactory.GetActionBuff(_player.Stats, bleed)); + + // Attack + _enemy.Targets.Add(_player); + var skillRow = + _tableSheets.SkillSheet.Values.First(bf => bf.Id == 100000); + var attack = new NormalAttack(skillRow, 100, 100, default, StatType.NONE); + var battleStatus = attack.Use( + _enemy, + 0, + BuffFactory.GetBuffs( + _enemy.Stats, + attack, + _tableSheets.SkillBuffSheet, + _tableSheets.StatBuffSheet, + _tableSheets.SkillActionBuffSheet, + _tableSheets.ActionBuffSheet + ), + false); + + // keep debuff + Assert.Equal(2, _player.Buffs.Count); + Assert.True(battleStatus.SkillInfos.First().Affected); + + // Attack + _player.Targets.Add(_enemy); + battleStatus = attack.Use( + _player, + 0, + BuffFactory.GetBuffs( + _player.Stats, + attack, + _tableSheets.SkillBuffSheet, + _tableSheets.StatBuffSheet, + _tableSheets.SkillActionBuffSheet, + _tableSheets.ActionBuffSheet + ), + false); + + // keep debuff + Assert.Equal(2, _player.Buffs.Count); + Assert.True(battleStatus.SkillInfos.First().Affected); + } + [Theory] // Buff [InlineData(SkillType.Buff, true)] From 233e34ec64065f8d79291e4c72b9d92cdd8d42fb Mon Sep 17 00:00:00 2001 From: hyeon Date: Wed, 13 Mar 2024 21:15:00 +0900 Subject: [PATCH 12/17] Add ShatterStrikeMaxDamage to IArenaSimulator to remove casting --- Lib9c/Arena/ArenaSimulatorV1.cs | 2 ++ Lib9c/Arena/ArenaSimulatorV2.cs | 1 + Lib9c/Arena/ArenaSimulatorV3.cs | 1 + Lib9c/Arena/ArenaSimulatorV4.cs | 1 + Lib9c/Arena/IArenaSimulator.cs | 1 + Lib9c/Model/Skill/Arena/ArenaAttackSkill.cs | 2 +- 6 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Lib9c/Arena/ArenaSimulatorV1.cs b/Lib9c/Arena/ArenaSimulatorV1.cs index e61d0e5094..92669eff35 100644 --- a/Lib9c/Arena/ArenaSimulatorV1.cs +++ b/Lib9c/Arena/ArenaSimulatorV1.cs @@ -21,6 +21,8 @@ public class ArenaSimulatorV1 : IArenaSimulator public int Turn { get; private set; } public ArenaLog Log { get; private set; } + public long ShatterStrikeMaxDamage { get; } + public ArenaSimulatorV1(IRandom random) { Random = random; diff --git a/Lib9c/Arena/ArenaSimulatorV2.cs b/Lib9c/Arena/ArenaSimulatorV2.cs index 6e0c9bba74..8eb94c8e4e 100644 --- a/Lib9c/Arena/ArenaSimulatorV2.cs +++ b/Lib9c/Arena/ArenaSimulatorV2.cs @@ -19,6 +19,7 @@ public class ArenaSimulatorV2 : IArenaSimulator public IRandom Random { get; } public int Turn { get; private set; } public ArenaLog Log { get; private set; } + public long ShatterStrikeMaxDamage { get; } public ArenaSimulatorV2(IRandom random) { diff --git a/Lib9c/Arena/ArenaSimulatorV3.cs b/Lib9c/Arena/ArenaSimulatorV3.cs index 8c89f73e9d..0f57a4405b 100644 --- a/Lib9c/Arena/ArenaSimulatorV3.cs +++ b/Lib9c/Arena/ArenaSimulatorV3.cs @@ -19,6 +19,7 @@ public class ArenaSimulatorV3 : IArenaSimulator public IRandom Random { get; } public int Turn { get; private set; } public ArenaLog Log { get; private set; } + public long ShatterStrikeMaxDamage { get; } public ArenaSimulatorV3(IRandom random) { diff --git a/Lib9c/Arena/ArenaSimulatorV4.cs b/Lib9c/Arena/ArenaSimulatorV4.cs index 01db3b1d03..c6c18bd6f3 100644 --- a/Lib9c/Arena/ArenaSimulatorV4.cs +++ b/Lib9c/Arena/ArenaSimulatorV4.cs @@ -19,6 +19,7 @@ public class ArenaSimulatorV4 : IArenaSimulator public IRandom Random { get; } public int Turn { get; private set; } public ArenaLog Log { get; private set; } + public long ShatterStrikeMaxDamage { get; } public ArenaSimulatorV4(IRandom random) { diff --git a/Lib9c/Arena/IArenaSimulator.cs b/Lib9c/Arena/IArenaSimulator.cs index b8804bedea..29024a6cb3 100644 --- a/Lib9c/Arena/IArenaSimulator.cs +++ b/Lib9c/Arena/IArenaSimulator.cs @@ -9,5 +9,6 @@ public interface IArenaSimulator public ArenaLog Log { get; } public IRandom Random { get; } public int Turn { get; } + public long ShatterStrikeMaxDamage { get; } } } diff --git a/Lib9c/Model/Skill/Arena/ArenaAttackSkill.cs b/Lib9c/Model/Skill/Arena/ArenaAttackSkill.cs index 9899999c87..0cd7cfc7b9 100644 --- a/Lib9c/Model/Skill/Arena/ArenaAttackSkill.cs +++ b/Lib9c/Model/Skill/Arena/ArenaAttackSkill.cs @@ -66,7 +66,7 @@ protected ArenaAttackSkill( if (SkillRow.SkillCategory is SkillCategory.ShatterStrike) { damage = Math.Clamp(damage, - 1, ((ArenaSimulator)caster.Simulator).ShatterStrikeMaxDamage); + 1, caster.Simulator.ShatterStrikeMaxDamage); } target.CurrentHP -= damage; From ab0efff30cca0f0c0b03fdba411a2d5daf1b22b6 Mon Sep 17 00:00:00 2001 From: hyeon Date: Wed, 13 Mar 2024 21:15:57 +0900 Subject: [PATCH 13/17] Update simulators to set ShatterStrike max damage - Pass value directly to simulator from action --- Lib9c/Action/EventDungeonBattle.cs | 3 ++- Lib9c/Action/HackAndSlash.cs | 3 ++- Lib9c/Action/Raid.cs | 5 +++-- Lib9c/Battle/RaidSimulator.cs | 5 ++++- Lib9c/Battle/Simulator.cs | 11 +++++------ Lib9c/Battle/StageSimulator.cs | 8 ++++++-- 6 files changed, 22 insertions(+), 13 deletions(-) diff --git a/Lib9c/Action/EventDungeonBattle.cs b/Lib9c/Action/EventDungeonBattle.cs index 978f542cf9..e14725ed45 100644 --- a/Lib9c/Action/EventDungeonBattle.cs +++ b/Lib9c/Action/EventDungeonBattle.cs @@ -366,7 +366,8 @@ is Bencodex.Types.List serializedEventDungeonInfoList stageRow, sheets.GetSheet(), PlayCount), - collectionModifiers); + collectionModifiers, + shatterStrikeMaxDamage: gameConfigState.ShatterStrikeMaxDamage); simulator.Simulate(); sw.Stop(); Log.Verbose( diff --git a/Lib9c/Action/HackAndSlash.cs b/Lib9c/Action/HackAndSlash.cs index a2b05c2592..17d2060394 100644 --- a/Lib9c/Action/HackAndSlash.cs +++ b/Lib9c/Action/HackAndSlash.cs @@ -494,7 +494,8 @@ public IWorld Execute( costumeStatSheet, rewards, collectionModifiers, - false); + false, + gameConfigState.ShatterStrikeMaxDamage); sw.Stop(); Log.Verbose("{AddressesHex} {Source} HAS {Process} from #{BlockIndex}: {Elapsed}", addressesHex, source, "Initialize Simulator", blockIndex, sw.Elapsed.TotalMilliseconds); diff --git a/Lib9c/Action/Raid.cs b/Lib9c/Action/Raid.cs index 0001f8276d..31bd4c3f96 100644 --- a/Lib9c/Action/Raid.cs +++ b/Lib9c/Action/Raid.cs @@ -231,8 +231,9 @@ public override IWorld Execute(IActionContext context) runeStates, raidSimulatorSheets, sheets.GetSheet(), - collectionModifiers - ); + collectionModifiers, + shatterStrikeMaxDamage: gameConfigState.ShatterStrikeMaxDamage + ); simulator.Simulate(); avatarState.inventory = simulator.Player.Inventory; diff --git a/Lib9c/Battle/RaidSimulator.cs b/Lib9c/Battle/RaidSimulator.cs index bb4b27137c..ac2eeaf947 100644 --- a/Lib9c/Battle/RaidSimulator.cs +++ b/Lib9c/Battle/RaidSimulator.cs @@ -36,7 +36,10 @@ public RaidSimulator( List runeStates, RaidSimulatorSheets simulatorSheets, CostumeStatSheet costumeStatSheet, - List collectionModifiers) : base(random, avatarState, foods, simulatorSheets) + List collectionModifiers, + long shatterStrikeMaxDamage = 400_000 + ) : base(random, avatarState, foods, simulatorSheets, + shatterStrikeMaxDamage: shatterStrikeMaxDamage) { var runeOptionSheet = simulatorSheets.RuneOptionSheet; var skillSheet = simulatorSheets.SkillSheet; diff --git a/Lib9c/Battle/Simulator.cs b/Lib9c/Battle/Simulator.cs index 964e303c05..943805c2eb 100644 --- a/Lib9c/Battle/Simulator.cs +++ b/Lib9c/Battle/Simulator.cs @@ -1,12 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; -using Bencodex.Types; using Libplanet.Action; using Nekoyume.Model; using Nekoyume.Model.BattleStatus; using Nekoyume.Model.Item; -using Nekoyume.Model.Skill; using Nekoyume.Model.State; using Nekoyume.TableData; using Priority_Queue; @@ -51,13 +49,14 @@ protected Simulator(IRandom random, protected Simulator(IRandom random, AvatarState avatarState, List foods, - SimulatorSheets simulatorSheets, bool logEvent = true) + SimulatorSheets simulatorSheets, + bool logEvent = true, + long shatterStrikeMaxDamage = 400_000 // 400k is initial limit of ShatterStrike. Use this as default + ) : this(random, new Player(avatarState, simulatorSheets), foods, simulatorSheets) { LogEvent = logEvent; - ShatterStrikeMaxDamage = - new GameConfigState((Text)simulatorSheets.GameConiConfigSheet.Serialize()) - .ShatterStrikeMaxDamage; + ShatterStrikeMaxDamage = shatterStrikeMaxDamage; } protected Simulator( diff --git a/Lib9c/Battle/StageSimulator.cs b/Lib9c/Battle/StageSimulator.cs index 6222cf839e..ffaed83839 100644 --- a/Lib9c/Battle/StageSimulator.cs +++ b/Lib9c/Battle/StageSimulator.cs @@ -49,13 +49,17 @@ public StageSimulator(IRandom random, CostumeStatSheet costumeStatSheet, List waveRewards, List collectionModifiers, - bool logEvent = true) + bool logEvent = true, + long shatterStrikeMaxDamage = 400_000 + ) : base( random, avatarState, foods, simulatorSheets, - logEvent) + logEvent, + shatterStrikeMaxDamage + ) { var runeOptionSheet = simulatorSheets.RuneOptionSheet; var skillSheet = simulatorSheets.SkillSheet; From 4bfcd3bf0230eea4ddd0e9d3938b029fafb66891 Mon Sep 17 00:00:00 2001 From: hyeon Date: Wed, 13 Mar 2024 21:12:52 +0900 Subject: [PATCH 14/17] Add more Dispel tests - Check dispel not to affect for all actions --- .../Model/Skill/Arena/ArenaCombatTest.cs | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/.Lib9c.Tests/Model/Skill/Arena/ArenaCombatTest.cs b/.Lib9c.Tests/Model/Skill/Arena/ArenaCombatTest.cs index 97307544db..04e42a5aaf 100644 --- a/.Lib9c.Tests/Model/Skill/Arena/ArenaCombatTest.cs +++ b/.Lib9c.Tests/Model/Skill/Arena/ArenaCombatTest.cs @@ -194,5 +194,79 @@ public void DispelOnDuration_Affect() Assert.Equal(2, challenger.Buffs.Count); Assert.True(battleStatus.SkillInfos.First().Affected); } + + [Fact] + public void DispelOnDuration_Nothing() + { + var arenaSheets = _tableSheets.GetArenaSimulatorSheets(); + var myDigest = new ArenaPlayerDigest(_avatar1, _arenaAvatar1); + var enemyDigest = new ArenaPlayerDigest(_avatar2, _arenaAvatar2); + var simulator = new ArenaSimulator(new TestRandom()); + var challenger = new ArenaCharacter( + simulator, + myDigest, + arenaSheets, + simulator.HpModifier, + new List() + ); + var enemy = new ArenaCharacter( + simulator, + enemyDigest, + arenaSheets, + simulator.HpModifier, + new List() + ); + + // Use Dispel first + var dispel = _tableSheets.ActionBuffSheet.Values.First(bf => bf.Id == ActionBuffId); + challenger.AddBuff(BuffFactory.GetActionBuff(challenger.Stats, dispel)); + Assert.Single(challenger.Buffs); + + // Use Bleed + var bleed = _tableSheets.ActionBuffSheet.Values.First(bf => bf.Id == 600001); + challenger.AddBuff(BuffFactory.GetActionBuff(challenger.Stats, bleed)); + Assert.Equal(2, challenger.Buffs.Count); + + // NormalAttack to test. This must not remove debuff by dispel + var attackRow = + _tableSheets.SkillSheet.Values.First(bf => bf.Id == 100000); + var attack = new ArenaNormalAttack(attackRow, 100, 100, 0, StatType.NONE); + + // Hit + var battleStatus = attack.Use( + enemy, + challenger, + simulator.Turn, + BuffFactory.GetBuffs( + challenger.Stats, + attack, + _tableSheets.SkillBuffSheet, + _tableSheets.StatBuffSheet, + _tableSheets.SkillActionBuffSheet, + _tableSheets.ActionBuffSheet + ) + ); + Assert.Equal(2, challenger.Buffs.Count); + Assert.True(battleStatus.SkillInfos.First().Affected); + Assert.Contains(600001, challenger.Buffs.Values.Select(bf => bf.BuffInfo.Id)); + + // Attack + battleStatus = attack.Use( + challenger, + enemy, + simulator.Turn, + BuffFactory.GetBuffs( + challenger.Stats, + attack, + _tableSheets.SkillBuffSheet, + _tableSheets.StatBuffSheet, + _tableSheets.SkillActionBuffSheet, + _tableSheets.ActionBuffSheet + ) + ); + Assert.Equal(2, challenger.Buffs.Count); + Assert.True(battleStatus.SkillInfos.First().Affected); + Assert.Contains(600001, challenger.Buffs.Values.Select(bf => bf.BuffInfo.Id)); + } } } From 508a1770befed9aa902d12504ddc38db19862ce0 Mon Sep 17 00:00:00 2001 From: hyeon Date: Wed, 13 Mar 2024 21:15:57 +0900 Subject: [PATCH 15/17] Update simulators to set ShatterStrike max damage - Pass value directly to simulator from action --- .Lib9c.Tests/Model/Skill/Arena/ArenaShatterStrikeTest.cs | 6 +++++- .Lib9c.Tests/Model/Skill/ShatterStrikeTest.cs | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.Lib9c.Tests/Model/Skill/Arena/ArenaShatterStrikeTest.cs b/.Lib9c.Tests/Model/Skill/Arena/ArenaShatterStrikeTest.cs index fcb3dbd68d..545852abc8 100644 --- a/.Lib9c.Tests/Model/Skill/Arena/ArenaShatterStrikeTest.cs +++ b/.Lib9c.Tests/Model/Skill/Arena/ArenaShatterStrikeTest.cs @@ -56,7 +56,11 @@ public void Use(int hpModifier, int ratioBp, bool expectedEnemyDead) { var gameConfigState = new GameConfigState((Text)_tableSheets.GameConfigSheet.Serialize()); - var simulator = new ArenaSimulator(new TestRandom(), hpModifier: hpModifier); + var simulator = new ArenaSimulator( + new TestRandom(), + hpModifier: hpModifier, + shatterStrikeMaxDamage: gameConfigState.ShatterStrikeMaxDamage + ); var myDigest = new ArenaPlayerDigest(_avatar1, _arenaAvatar1); var enemyDigest = new ArenaPlayerDigest(_avatar2, _arenaAvatar2); var arenaSheets = _tableSheets.GetArenaSimulatorSheets(); diff --git a/.Lib9c.Tests/Model/Skill/ShatterStrikeTest.cs b/.Lib9c.Tests/Model/Skill/ShatterStrikeTest.cs index 13610d7b45..bb7283608a 100644 --- a/.Lib9c.Tests/Model/Skill/ShatterStrikeTest.cs +++ b/.Lib9c.Tests/Model/Skill/ShatterStrikeTest.cs @@ -71,7 +71,8 @@ public void Use(int enemyHp, int ratioBp, bool expectedEnemyDead, bool copyChara _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), new List(), - copyCharacter + copyCharacter, + shatterStrikeMaxDamage: gameConfigState.ShatterStrikeMaxDamage ); var player = new Player(avatarState, simulator); var enemyRow = _tableSheets.CharacterSheet.OrderedList From 51574feaf912f911c125e2170491be7d24419f29 Mon Sep 17 00:00:00 2001 From: hyeon Date: Thu, 14 Mar 2024 11:49:36 +0900 Subject: [PATCH 16/17] Add test to check Dispel failes --- .Lib9c.Tests/Model/Skill/CombatTest.cs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/.Lib9c.Tests/Model/Skill/CombatTest.cs b/.Lib9c.Tests/Model/Skill/CombatTest.cs index baca0e460c..27711d8b49 100644 --- a/.Lib9c.Tests/Model/Skill/CombatTest.cs +++ b/.Lib9c.Tests/Model/Skill/CombatTest.cs @@ -16,6 +16,7 @@ namespace Lib9c.Tests.Model.Skill public class CombatTest { private readonly TableSheets _tableSheets; + private readonly AvatarState _avatarState; private readonly Player _player; private readonly Enemy _enemy; @@ -25,7 +26,7 @@ public CombatTest() _tableSheets = new TableSheets(csv); var gameConfigState = new GameConfigState(csv[nameof(GameConfigSheet)]); - var avatarState = new AvatarState( + _avatarState = new AvatarState( default, default, 0, @@ -40,7 +41,7 @@ public CombatTest() _tableSheets.EquipmentItemSetEffectSheet); var simulator = new TestSimulator( new TestRandom(), - avatarState, + _avatarState, new List(), _tableSheets.GetSimulatorSheets()); _player.Simulator = simulator; @@ -138,16 +139,23 @@ public void Bleed() } [Theory] - [InlineData(700009, new[] { 600001 })] - [InlineData(700010, new[] { 600001, 704000 })] - public void DispelOnUse(int dispelId, int[] debuffIdList) + [InlineData(700009, 50, 1, new[] { 600001 }, new[] { 600001 })] + [InlineData(700009, 100, 0, new[] { 600001 }, new[] { 707000 })] + [InlineData(700010, 100, 0, new[] { 600001, 704000 }, new[] { 708000 })] + public void DispelOnUse(int dispelId, int chance, int seed, int[] debuffIdList, int[] expectedResult) { + var simulator = new TestSimulator( + new TestRandom(seed), + _avatarState, + new List(), + _tableSheets.GetSimulatorSheets()); var actionBuffSheet = _tableSheets.ActionBuffSheet; // Add Debuff foreach (var debuffId in debuffIdList) { - var debuff = actionBuffSheet.Values.First(bf => bf.Id == debuffId); // 600001 is bleed + var debuff = + actionBuffSheet.Values.First(bf => bf.Id == debuffId); // 600001 is bleed _player.AddBuff(BuffFactory.GetActionBuff(_player.Stats, debuff)); } @@ -160,7 +168,7 @@ public void DispelOnUse(int dispelId, int[] debuffIdList) abf => abf.SkillId == dispelId) .BuffIds.First() ); - var dispel = new BuffSkill(skillRow, 0, 100, 0, StatType.NONE); + var dispel = new BuffSkill(skillRow, 0, chance, 0, StatType.NONE); var battleStatus = dispel.Use( _player, 0, @@ -176,7 +184,7 @@ public void DispelOnUse(int dispelId, int[] debuffIdList) Assert.NotNull(battleStatus); // Remove Bleed, add Dispel Assert.Single(_player.Buffs); - Assert.Equal(dispelRow.GroupId, _player.Buffs.First().Value.BuffInfo.GroupId); + Assert.Equal(expectedResult, _player.Buffs.Values.Select(bf => bf.BuffInfo.GroupId).ToArray()); } [Fact] From 4fcd51a9e524e75104f24737a765812dcedc4231 Mon Sep 17 00:00:00 2001 From: hyeon Date: Thu, 14 Mar 2024 14:20:00 +0900 Subject: [PATCH 17/17] Fix broken test --- .Lib9c.Tests/Model/Skill/CombatTest.cs | 27 +++++++++----------------- Lib9c/Model/Character/CharacterBase.cs | 2 +- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/.Lib9c.Tests/Model/Skill/CombatTest.cs b/.Lib9c.Tests/Model/Skill/CombatTest.cs index 27711d8b49..9b3e223e46 100644 --- a/.Lib9c.Tests/Model/Skill/CombatTest.cs +++ b/.Lib9c.Tests/Model/Skill/CombatTest.cs @@ -139,9 +139,9 @@ public void Bleed() } [Theory] - [InlineData(700009, 50, 1, new[] { 600001 }, new[] { 600001 })] + [InlineData(700009, 50, 3, new[] { 600001 }, new[] { 600001, 707000 })] [InlineData(700009, 100, 0, new[] { 600001 }, new[] { 707000 })] - [InlineData(700010, 100, 0, new[] { 600001, 704000 }, new[] { 708000 })] + [InlineData(700010, 100, 0, new[] { 600001, 704000 }, new[] { 707000 })] public void DispelOnUse(int dispelId, int chance, int seed, int[] debuffIdList, int[] expectedResult) { var simulator = new TestSimulator( @@ -149,6 +149,7 @@ public void DispelOnUse(int dispelId, int chance, int seed, int[] debuffIdList, _avatarState, new List(), _tableSheets.GetSimulatorSheets()); + _player.Simulator = simulator; var actionBuffSheet = _tableSheets.ActionBuffSheet; // Add Debuff @@ -162,28 +163,18 @@ public void DispelOnUse(int dispelId, int chance, int seed, int[] debuffIdList, Assert.Equal(debuffIdList.Length, _player.Buffs.Count()); // Use Dispel - var skillRow = _tableSheets.SkillSheet.Values.First(bf => bf.Id == dispelId); - var dispelRow = _tableSheets.ActionBuffSheet.Values.First( - bf => bf.Id == _tableSheets.SkillActionBuffSheet.OrderedList.First( - abf => abf.SkillId == dispelId) - .BuffIds.First() - ); - var dispel = new BuffSkill(skillRow, 0, chance, 0, StatType.NONE); + var actionBuffRow = new ActionBuffSheet.Row(); + actionBuffRow.Set(new[] { "707000", "707000", chance.ToString(), "0", "Self", "Dispel", "Normal", "0" }); + var dispelRow = _tableSheets.SkillSheet.Values.First(bf => bf.Id == dispelId); + var dispel = new BuffSkill(dispelRow, 0, chance, 0, StatType.NONE); var battleStatus = dispel.Use( _player, 0, - BuffFactory.GetBuffs( - _player.Stats, - dispel, - _tableSheets.SkillBuffSheet, - _tableSheets.StatBuffSheet, - _tableSheets.SkillActionBuffSheet, - _tableSheets.ActionBuffSheet - ), + new List() { new Dispel(actionBuffRow) }, false); Assert.NotNull(battleStatus); // Remove Bleed, add Dispel - Assert.Single(_player.Buffs); + Assert.Equal(expectedResult.Length, _player.Buffs.Count); Assert.Equal(expectedResult, _player.Buffs.Values.Select(bf => bf.BuffInfo.GroupId).ToArray()); } diff --git a/Lib9c/Model/Character/CharacterBase.cs b/Lib9c/Model/Character/CharacterBase.cs index 1e87d7308c..7dc8cf95fe 100644 --- a/Lib9c/Model/Character/CharacterBase.cs +++ b/Lib9c/Model/Character/CharacterBase.cs @@ -401,7 +401,7 @@ protected virtual void EndTurn() dispelList = Buffs.Values.Where( bff => bff.IsDebuff() && Simulator.Random.Next(0, 100) < - action.RowData.Chance).ToList(); + dispel.BuffInfo.Chance).ToList(); foreach (var bff in dispelList) {