diff --git a/.Lib9c.Tests/Action/Scenario/AuraScenarioTest.cs b/.Lib9c.Tests/Action/Scenario/AuraScenarioTest.cs index 35358695e4..49382047cb 100644 --- a/.Lib9c.Tests/Action/Scenario/AuraScenarioTest.cs +++ b/.Lib9c.Tests/Action/Scenario/AuraScenarioTest.cs @@ -257,7 +257,8 @@ public void Arena() enemyArenaPlayerDigest, _tableSheets.GetArenaSimulatorSheets(), new List(), - new List()); + new List(), + _tableSheets.DeBuffLimitSheet); // Check player, enemy equip aura foreach (var spawn in log.OfType()) { diff --git a/.Lib9c.Tests/Model/ArenaSimulatorTest.cs b/.Lib9c.Tests/Model/ArenaSimulatorTest.cs index 5bb6bfd4ac..0261c3e87f 100644 --- a/.Lib9c.Tests/Model/ArenaSimulatorTest.cs +++ b/.Lib9c.Tests/Model/ArenaSimulatorTest.cs @@ -78,7 +78,8 @@ public void Simulate() { new (StatType.DEF, StatModifier.OperationType.Add, 1), new (StatType.HP, StatModifier.OperationType.Add, 100), - } + }, + _tableSheets.DeBuffLimitSheet ); CharacterSheet.Row row = _tableSheets.CharacterSheet[GameConfig.DefaultAvatarCharacterId]; @@ -133,7 +134,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, new List(), new List()); + var log = simulator.Simulate(myDigest, enemyDigest, arenaSheets, new List(), new List(), _tableSheets.DeBuffLimitSheet); var expectedHpModifier = modifier ?? 2; Assert.Equal(_random, simulator.Random); @@ -178,7 +179,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, new List(), new List()); + var unskilledLog = simulator.Simulate(myDigest, enemyDigest, arenaSheets, new List(), new List(), _tableSheets.DeBuffLimitSheet); // foreach (var log in unskilledLog) // { // _testOutputHelper.WriteLine($"{log.Character.Id} :: {log}"); @@ -231,7 +232,7 @@ public void TestSpeedModifierBySkill() myDigest = new ArenaPlayerDigest(_avatarState1, arenaAvatarState1); enemyDigest = new ArenaPlayerDigest(_avatarState2, arenaAvatarState2); arenaSheets = _tableSheets.GetArenaSimulatorSheets(); - var skilledLog = simulator.Simulate(myDigest, enemyDigest, arenaSheets, new List(), new List()); + var skilledLog = simulator.Simulate(myDigest, enemyDigest, arenaSheets, new List(), new List(), _tableSheets.DeBuffLimitSheet); // foreach (var log in skilledLog) // { // _testOutputHelper.WriteLine($"{log.Character.Id} :: {log}"); @@ -273,7 +274,7 @@ public void Thorns() 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 log = simulator.Simulate(myDigest, enemyDigest, arenaSheets, new List(), new List(), _tableSheets.DeBuffLimitSheet, true); var ticks = log.Events .OfType() .ToList(); @@ -335,7 +336,7 @@ public void Bleed() var enemyDigest = new ArenaPlayerDigest(avatarState2, arenaAvatarState2); enemyDigest.Runes.Add(rune); var arenaSheets = _tableSheets.GetArenaSimulatorSheets(); - var log = simulator.Simulate(myDigest, enemyDigest, arenaSheets, modifiers, modifiers, true); + var log = simulator.Simulate(myDigest, enemyDigest, arenaSheets, modifiers, modifiers, _tableSheets.DeBuffLimitSheet, true); var spawns = log.Events.OfType().ToList(); Assert.All(spawns, spawn => Assert.Equal(totalAtk, spawn.Character.ATK)); var ticks = log.Events diff --git a/.Lib9c.Tests/Model/BattleLogTest.cs b/.Lib9c.Tests/Model/BattleLogTest.cs index e5311fbff0..24aac3acd3 100644 --- a/.Lib9c.Tests/Model/BattleLogTest.cs +++ b/.Lib9c.Tests/Model/BattleLogTest.cs @@ -53,7 +53,8 @@ public void IsClearBeforeSimulate() _random, _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), - new List() + new List(), + _tableSheets.DeBuffLimitSheet ); Assert.False(simulator.Log.IsClear); } diff --git a/.Lib9c.Tests/Model/CharacterStatsTest.cs b/.Lib9c.Tests/Model/CharacterStatsTest.cs new file mode 100644 index 0000000000..6b14d0a97a --- /dev/null +++ b/.Lib9c.Tests/Model/CharacterStatsTest.cs @@ -0,0 +1,35 @@ +namespace Lib9c.Tests.Model +{ + using Nekoyume; + using Nekoyume.Model.Buff; + using Nekoyume.Model.Stat; + using Nekoyume.TableData; + using Xunit; + + public class CharacterStatsTest + { + private readonly TableSheets _tableSheets; + + public CharacterStatsTest() + { + _tableSheets = new TableSheets(TableSheetsImporter.ImportSheets()); + } + + [Fact] + public void DeBuffLimit() + { + var stats = + new CharacterStats( + _tableSheets.CharacterSheet[GameConfig.DefaultAvatarCharacterId], + 1); + var deBuffLimitSheet = new DeBuffLimitSheet(); + deBuffLimitSheet.Set("id,stat_type,percentage\n1,DEF,-50"); + var def = stats.DEF; + var deBuff = new StatBuff(_tableSheets.StatBuffSheet[503012]); + stats.AddBuff(deBuff, deBuffLimitSheet: deBuffLimitSheet); + var limitModifier = + new StatModifier(StatType.DEF, StatModifier.OperationType.Percentage, -50); + Assert.Equal(limitModifier.GetModifiedAll(def), stats.DEF); + } + } +} diff --git a/.Lib9c.Tests/Model/PlayerTest.cs b/.Lib9c.Tests/Model/PlayerTest.cs index e920816712..b88c6158e8 100644 --- a/.Lib9c.Tests/Model/PlayerTest.cs +++ b/.Lib9c.Tests/Model/PlayerTest.cs @@ -61,7 +61,8 @@ public void TickAlive() _random, _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), - new List() + new List(), + _tableSheets.DeBuffLimitSheet ); var player = simulator.Player; var enemy = new Enemy(player, _tableSheets.CharacterSheet.Values.First(), 1); @@ -95,7 +96,8 @@ public void TickDead() _random, _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), - new List() + new List(), + _tableSheets.DeBuffLimitSheet ); var player = simulator.Player; var enemy = new Enemy(player, _tableSheets.CharacterSheet.Values.First(), 1); @@ -146,7 +148,8 @@ public void UseDoubleAttack(SkillCategory skillCategory) _random, _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), - new List() + new List(), + _tableSheets.DeBuffLimitSheet ); var player = simulator.Player; @@ -197,7 +200,8 @@ public void UseAuraSkill() _random, _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), - new List() + new List(), + new DeBuffLimitSheet() ); var player = simulator.Player; var enemy = new Enemy(player, _tableSheets.CharacterSheet.Values.First(), 1); @@ -252,7 +256,8 @@ public void UseAuraBuffWithFood() _random, _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), - new List() + new List(), + new DeBuffLimitSheet() ); var player = simulator.Player; var enemy = new Enemy(player, _tableSheets.CharacterSheet.Values.First(), 1); @@ -380,7 +385,8 @@ public void GetExpV3(int nextLevel, bool log) _random, _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), - new List() + new List(), + _tableSheets.DeBuffLimitSheet ); var player = simulator.Player; Assert.Empty(player.eventMap); @@ -459,7 +465,8 @@ public void GetStun() _random, _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), - new List() + new List(), + _tableSheets.DeBuffLimitSheet ); var player = simulator.Player; var enemy = new Enemy(player, _tableSheets.CharacterSheet.Values.First(), 1); @@ -528,7 +535,8 @@ public void GiveStun() _random, _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), - new List() + new List(), + _tableSheets.DeBuffLimitSheet ); var skill = SkillFactory.Get(_tableSheets.SkillSheet[700004], 0, 100, 0, StatType.NONE); skill.CustomField = new SkillCustomField { BuffDuration = 2 }; @@ -611,7 +619,8 @@ public void Vampiric(int duration, int percent) _random, _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), - new List() + new List(), + _tableSheets.DeBuffLimitSheet ); var player = simulator.Player; var enemy = new Enemy( @@ -687,59 +696,45 @@ public void StatsLayerTest() _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); + // costume.equipped = true; + // _avatarState.inventory.AddItem(costume); var foodRow = _tableSheets.ConsumableItemSheet.Values.First(r => r.Stats.Any(s => s.StatType == StatType.ATK)); 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.ATK, player.Stats.BaseATK + player.Stats.EquipmentStats.ATK); - // BaseAtk 20, EquipmentStats 1 - // Assert.Equal(21, player.ATK); - var equipmentLayerAtk = player.ATK; - - // Update consumable stats - player.Use(new List - { - 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 var runeId = 10002; var runeState = new RuneState(runeId); runeState.LevelUp(); Assert.Equal(1, runeState.Level); - var runeStates = new List { runeState, }; - player.SetRuneStats(runeStates, _tableSheets.RuneOptionSheet); - 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 simulator = new StageSimulator( + _random, + _avatarState, + new List() + { + food.ItemId, + }, + runeStates, + new List(), + 1, + 1, + _tableSheets.StageSheet[1], + _tableSheets.StageWaveSheet[1], + false, + 20, + _tableSheets.GetSimulatorSheets(), + _tableSheets.EnemySkillSheet, + new CostumeStatSheet(), + new List(), + new List(), + _tableSheets.DeBuffLimitSheet + ); + var player = simulator.Player; var costumeLayerAtk = player.ATK; // Update collection stat @@ -808,7 +803,7 @@ public void StatsLayerTest() var addBuffAtk = addBuffModifier.GetModifiedValue(costumeLayerAtk); Assert.Equal(10, addBuffAtk); - player.Stats.SetBuffs(statBuffs); + player.Stats.SetBuffs(statBuffs, _tableSheets.DeBuffLimitSheet); Assert.Equal(player.ATK, collectionLayerAtk + addBuffAtk + percentageBuffAtk); // 20 + 1 + 18 + 1829 + 235 + 100 + 1662 // Assert.Equal(3865, player.ATK); @@ -912,7 +907,7 @@ public void IncreaseHpForArena() statBuffs.Add(percentageBuff); var percentageModifier = percentageBuff.GetModifier(); var percentageBuffAtk = (long)percentageModifier.GetModifiedValue(arenaHp); - player.Stats.SetBuffs(statBuffs); + player.Stats.SetBuffs(statBuffs, _tableSheets.DeBuffLimitSheet); Assert.Equal(arenaHp + percentageBuffAtk, player.HP); Assert.Equal(arenaHp, player.Stats.StatWithoutBuffs.HP); } diff --git a/.Lib9c.Tests/Model/RaidSimulatorV3Test.cs b/.Lib9c.Tests/Model/RaidSimulatorV3Test.cs index 73e70edb92..3eed9b0c3c 100644 --- a/.Lib9c.Tests/Model/RaidSimulatorV3Test.cs +++ b/.Lib9c.Tests/Model/RaidSimulatorV3Test.cs @@ -55,7 +55,8 @@ public void Simulate() new List { new (StatType.DEF, StatModifier.OperationType.Percentage, 100), - }); + }, + _tableSheets.DeBuffLimitSheet); 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); @@ -96,7 +97,8 @@ public void TestSpeedMultiplierBySkill() null, _tableSheets.GetRaidSimulatorSheets(), _tableSheets.CostumeStatSheet, - new List() + new List(), + _tableSheets.DeBuffLimitSheet ); var player = simulator.Player; var unskilledLogs = simulator.Simulate(); @@ -136,7 +138,8 @@ public void TestSpeedMultiplierBySkill() null, _tableSheets.GetRaidSimulatorSheets(), _tableSheets.CostumeStatSheet, - new List() + new List(), + _tableSheets.DeBuffLimitSheet ); player = simulator.Player; var skilledLogs = simulator.Simulate(); diff --git a/.Lib9c.Tests/Model/Skill/DoubleAttackTest.cs b/.Lib9c.Tests/Model/Skill/DoubleAttackTest.cs index 89320fb05b..a659e44768 100644 --- a/.Lib9c.Tests/Model/Skill/DoubleAttackTest.cs +++ b/.Lib9c.Tests/Model/Skill/DoubleAttackTest.cs @@ -98,6 +98,7 @@ bool copyCharacter _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), new List(), + _tableSheets.DeBuffLimitSheet, copyCharacter ); var player = new Player(avatarState, simulator) diff --git a/.Lib9c.Tests/Model/Skill/NormalAttackTest.cs b/.Lib9c.Tests/Model/Skill/NormalAttackTest.cs index a10dddb665..8e5b94126e 100644 --- a/.Lib9c.Tests/Model/Skill/NormalAttackTest.cs +++ b/.Lib9c.Tests/Model/Skill/NormalAttackTest.cs @@ -67,6 +67,7 @@ public void Use(bool copyCharacter) _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), new List(), + _tableSheets.DeBuffLimitSheet, copyCharacter ); var player = new Player(avatarState, simulator); @@ -130,7 +131,8 @@ public void FocusSkill() new TestRandom(seed), _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), - new List() + new List(), + _tableSheets.DeBuffLimitSheet ); var player = new Player(avatarState, simulator); @@ -178,7 +180,8 @@ public void FocusSkill() new TestRandom(seed), _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), - new List() + new List(), + _tableSheets.DeBuffLimitSheet ); player = new Player(avatarState, simulator); player.AddBuff(new Focus(_tableSheets.ActionBuffSheet.OrderedList.First(s => s.ActionBuffType == ActionBuffType.Focus))); diff --git a/.Lib9c.Tests/Model/Skill/ShatterStrikeTest.cs b/.Lib9c.Tests/Model/Skill/ShatterStrikeTest.cs index bb7283608a..a906c3f2fd 100644 --- a/.Lib9c.Tests/Model/Skill/ShatterStrikeTest.cs +++ b/.Lib9c.Tests/Model/Skill/ShatterStrikeTest.cs @@ -71,6 +71,7 @@ public void Use(int enemyHp, int ratioBp, bool expectedEnemyDead, bool copyChara _tableSheets.StageSheet[1], _tableSheets.MaterialItemSheet), new List(), + _tableSheets.DeBuffLimitSheet, copyCharacter, shatterStrikeMaxDamage: gameConfigState.ShatterStrikeMaxDamage ); diff --git a/.Lib9c.Tests/Model/StageSimulatorTest.cs b/.Lib9c.Tests/Model/StageSimulatorTest.cs index 6c8132a2ec..1eb8173aa7 100644 --- a/.Lib9c.Tests/Model/StageSimulatorTest.cs +++ b/.Lib9c.Tests/Model/StageSimulatorTest.cs @@ -69,7 +69,8 @@ public void Simulate() new List { new (StatType.ATK, StatModifier.OperationType.Add, 100), - } + }, + _tableSheets.DeBuffLimitSheet ); var player = simulator.Player; @@ -127,7 +128,8 @@ public void TestSpeedModifierBySkill() new TestRandom(1), _tableSheets.StageSheet[3], _tableSheets.MaterialItemSheet), - new List() + new List(), + _tableSheets.DeBuffLimitSheet ); var unskilledPlayer = simulator.Player; Assert.Contains(item, unskilledPlayer.Inventory.Equipments); @@ -179,7 +181,8 @@ public void TestSpeedModifierBySkill() new TestRandom(1), _tableSheets.StageSheet[3], _tableSheets.MaterialItemSheet), - new List() + new List(), + _tableSheets.DeBuffLimitSheet ); var skilledPlayer = simulator.Player; Assert.Contains(skilledItem, skilledPlayer.Inventory.Equipments); diff --git a/.Lib9c.Tests/TableSheets.cs b/.Lib9c.Tests/TableSheets.cs index 67d5497355..df043b3519 100644 --- a/.Lib9c.Tests/TableSheets.cs +++ b/.Lib9c.Tests/TableSheets.cs @@ -248,6 +248,8 @@ public StakeActionPointCoefficientSheet StakeActionPointCoefficientSheet public CollectionSheet CollectionSheet { get; private set; } + public DeBuffLimitSheet DeBuffLimitSheet { get; set; } + public void ItemSheetInitialize() { ItemSheet ??= new ItemSheet(); diff --git a/Lib9c/Action/BattleArena.cs b/Lib9c/Action/BattleArena.cs index 7a81ab4af6..b7f958979f 100644 --- a/Lib9c/Action/BattleArena.cs +++ b/Lib9c/Action/BattleArena.cs @@ -130,6 +130,7 @@ public override IWorld Execute(IActionContext context) typeof(EquipmentItemOptionSheet), typeof(MaterialItemSheet), typeof(RuneListSheet), + typeof(DeBuffLimitSheet), }; if (collectionExist) { @@ -379,6 +380,7 @@ public override IWorld Execute(IActionContext context) enemyRuneStates); var previousMyScore = myArenaScore.Score; var arenaSheets = sheets.GetArenaSimulatorSheets(); + var deBuffLimitSheet = sheets.GetSheet(); var winCount = 0; var defeatCount = 0; var rewards = new List(); @@ -412,6 +414,7 @@ public override IWorld Execute(IActionContext context) arenaSheets, modifiers[myAvatarAddress], modifiers[enemyAvatarAddress], + deBuffLimitSheet, true); if (log.Result.Equals(ArenaLog.ArenaResult.Win)) { diff --git a/Lib9c/Action/EventDungeonBattle.cs b/Lib9c/Action/EventDungeonBattle.cs index e14725ed45..8e75818e83 100644 --- a/Lib9c/Action/EventDungeonBattle.cs +++ b/Lib9c/Action/EventDungeonBattle.cs @@ -163,6 +163,7 @@ public override IWorld Execute(IActionContext context) typeof(CostumeStatSheet), typeof(MaterialItemSheet), typeof(RuneListSheet), + typeof(DeBuffLimitSheet), }; if (collectionExist) { @@ -346,6 +347,7 @@ is Bencodex.Types.List serializedEventDungeonInfoList } } + var deBuffLimitSheet = sheets.GetSheet(); var simulator = new StageSimulator( random, avatarState, @@ -367,6 +369,7 @@ is Bencodex.Types.List serializedEventDungeonInfoList sheets.GetSheet(), PlayCount), collectionModifiers, + deBuffLimitSheet, shatterStrikeMaxDamage: gameConfigState.ShatterStrikeMaxDamage); simulator.Simulate(); sw.Stop(); diff --git a/Lib9c/Action/HackAndSlash.cs b/Lib9c/Action/HackAndSlash.cs index 75617d6b7d..b2545d3a7c 100644 --- a/Lib9c/Action/HackAndSlash.cs +++ b/Lib9c/Action/HackAndSlash.cs @@ -177,6 +177,7 @@ public IWorld Execute( typeof(CrystalRandomBuffSheet), typeof(StakeActionPointCoefficientSheet), typeof(RuneListSheet), + typeof(DeBuffLimitSheet), }; if (collectionExist) { @@ -471,6 +472,8 @@ public IWorld Execute( collectionModifiers.AddRange(collectionSheet[collectionId].StatModifiers); } } + + var deBuffLimitSheet = sheets.GetSheet(); for (var i = 0; i < TotalPlayCount; i++) { var rewards = StageSimulator.GetWaveRewards(random, stageRow, materialItemSheet); @@ -494,6 +497,7 @@ public IWorld Execute( costumeStatSheet, rewards, collectionModifiers, + deBuffLimitSheet, false, gameConfigState.ShatterStrikeMaxDamage); sw.Stop(); diff --git a/Lib9c/Action/Raid.cs b/Lib9c/Action/Raid.cs index 31bd4c3f96..8c742a96bd 100644 --- a/Lib9c/Action/Raid.cs +++ b/Lib9c/Action/Raid.cs @@ -83,6 +83,7 @@ public override IWorld Execute(IActionContext context) typeof(WorldBossKillRewardSheet), typeof(RuneSheet), typeof(RuneListSheet), + typeof(DeBuffLimitSheet), }; if (collectionExist) { @@ -232,6 +233,7 @@ public override IWorld Execute(IActionContext context) raidSimulatorSheets, sheets.GetSheet(), collectionModifiers, + sheets.GetSheet(), shatterStrikeMaxDamage: gameConfigState.ShatterStrikeMaxDamage ); simulator.Simulate(); diff --git a/Lib9c/Arena/ArenaSimulator.cs b/Lib9c/Arena/ArenaSimulator.cs index b7b26e11fa..8dd985c762 100644 --- a/Lib9c/Arena/ArenaSimulator.cs +++ b/Lib9c/Arena/ArenaSimulator.cs @@ -23,6 +23,7 @@ public class ArenaSimulator : IArenaSimulator public int HpModifier { get; } public long ShatterStrikeMaxDamage { get; private set; } + public DeBuffLimitSheet DeBuffLimitSheet { get; private set; } public ArenaSimulator(IRandom random, int hpModifier = 2, @@ -41,9 +42,11 @@ public ArenaLog Simulate( ArenaSimulatorSheets sheets, List challengerCollectionModifiers, List enemyCollectionModifiers, + DeBuffLimitSheet deBuffLimitSheet, bool setExtraValueBuffBeforeGetBuffs = false) { Log = new ArenaLog(); + DeBuffLimitSheet = deBuffLimitSheet; var players = SpawnPlayers(this, challenger, enemy, sheets, Log, challengerCollectionModifiers, enemyCollectionModifiers, setExtraValueBuffBeforeGetBuffs); Turn = 1; diff --git a/Lib9c/Arena/IArenaSimulator.cs b/Lib9c/Arena/IArenaSimulator.cs index 29024a6cb3..370ab5a189 100644 --- a/Lib9c/Arena/IArenaSimulator.cs +++ b/Lib9c/Arena/IArenaSimulator.cs @@ -1,6 +1,7 @@ using Libplanet.Action; using Nekoyume.Model.BattleStatus.Arena; +using Nekoyume.TableData; namespace Nekoyume.Arena { @@ -10,5 +11,6 @@ public interface IArenaSimulator public IRandom Random { get; } public int Turn { get; } public long ShatterStrikeMaxDamage { get; } + public DeBuffLimitSheet DeBuffLimitSheet { get; } } } diff --git a/Lib9c/Battle/RaidSimulator.cs b/Lib9c/Battle/RaidSimulator.cs index ac2eeaf947..fd1a60574c 100644 --- a/Lib9c/Battle/RaidSimulator.cs +++ b/Lib9c/Battle/RaidSimulator.cs @@ -37,10 +37,12 @@ public RaidSimulator( RaidSimulatorSheets simulatorSheets, CostumeStatSheet costumeStatSheet, List collectionModifiers, + DeBuffLimitSheet deBuffLimitSheet, long shatterStrikeMaxDamage = 400_000 ) : base(random, avatarState, foods, simulatorSheets, shatterStrikeMaxDamage: shatterStrikeMaxDamage) { + DeBuffLimitSheet = deBuffLimitSheet; var runeOptionSheet = simulatorSheets.RuneOptionSheet; var skillSheet = simulatorSheets.SkillSheet; Player.ConfigureStats(costumeStatSheet, runeStates, runeOptionSheet, skillSheet, diff --git a/Lib9c/Battle/Simulator.cs b/Lib9c/Battle/Simulator.cs index 943805c2eb..cca3300271 100644 --- a/Lib9c/Battle/Simulator.cs +++ b/Lib9c/Battle/Simulator.cs @@ -28,6 +28,7 @@ public abstract class Simulator : ISimulator public readonly CharacterSheet CharacterSheet; public readonly CharacterLevelSheet CharacterLevelSheet; public readonly EquipmentItemSetEffectSheet EquipmentItemSetEffectSheet; + public DeBuffLimitSheet DeBuffLimitSheet { get; protected set; } public long ShatterStrikeMaxDamage { get; private set; } diff --git a/Lib9c/Battle/StageSimulator.cs b/Lib9c/Battle/StageSimulator.cs index ffaed83839..0097170cdd 100644 --- a/Lib9c/Battle/StageSimulator.cs +++ b/Lib9c/Battle/StageSimulator.cs @@ -49,6 +49,7 @@ public StageSimulator(IRandom random, CostumeStatSheet costumeStatSheet, List waveRewards, List collectionModifiers, + DeBuffLimitSheet deBuffLimitSheet, bool logEvent = true, long shatterStrikeMaxDamage = 400_000 ) @@ -61,6 +62,7 @@ public StageSimulator(IRandom random, shatterStrikeMaxDamage ) { + DeBuffLimitSheet = deBuffLimitSheet; var runeOptionSheet = simulatorSheets.RuneOptionSheet; var skillSheet = simulatorSheets.SkillSheet; Player.ConfigureStats(costumeStatSheet, runeStates, runeOptionSheet, skillSheet, diff --git a/Lib9c/Model/Character/ArenaCharacter.cs b/Lib9c/Model/Character/ArenaCharacter.cs index 0761e67890..6c8e8d36fc 100644 --- a/Lib9c/Model/Character/ArenaCharacter.cs +++ b/Lib9c/Model/Character/ArenaCharacter.cs @@ -728,7 +728,7 @@ private void RemoveBuffs() if (!isBuffRemoved) return; - Stats.SetBuffs(StatBuffs); + Stats.SetBuffs(StatBuffs, Simulator.DeBuffLimitSheet); } [Obsolete("Use RemoveBuffs")] @@ -750,7 +750,7 @@ private void RemoveBuffsV1() if (isApply) { - Stats.SetBuffs(StatBuffs); + Stats.SetBuffs(StatBuffs, Simulator.DeBuffLimitSheet); } } diff --git a/Lib9c/Model/Character/CharacterBase.cs b/Lib9c/Model/Character/CharacterBase.cs index 7dc8cf95fe..a3eca37b33 100644 --- a/Lib9c/Model/Character/CharacterBase.cs +++ b/Lib9c/Model/Character/CharacterBase.cs @@ -338,7 +338,7 @@ private void RemoveBuffs() if (!isBuffRemoved) return; - Stats.SetBuffs(StatBuffs); + Stats.SetBuffs(StatBuffs, Simulator.DeBuffLimitSheet); if (Simulator.LogEvent) { Simulator.Log.Add(new RemoveBuffs((CharacterBase) Clone())); @@ -378,7 +378,7 @@ protected virtual void EndTurn() { var clone = (StatBuff)stat.Clone(); Buffs[stat.RowData.GroupId] = clone; - Stats.AddBuff(clone, updateImmediate); + Stats.AddBuff(clone, Simulator.DeBuffLimitSheet, updateImmediate); break; } case ActionBuff action: diff --git a/Lib9c/Model/Stat/CharacterStats.cs b/Lib9c/Model/Stat/CharacterStats.cs index 56e1af1377..d69ca589af 100644 --- a/Lib9c/Model/Stat/CharacterStats.cs +++ b/Lib9c/Model/Stat/CharacterStats.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using Nekoyume.Model.Item; +using Nekoyume.Model.Skill; using Nekoyume.TableData; namespace Nekoyume.Model.Stat @@ -263,17 +264,18 @@ public CharacterStats SetRunes(IEnumerable value, bool updateImmed /// Set stats based on buffs. /// /// + /// /// /// public CharacterStats SetBuffs(IEnumerable value, - bool updateImmediate = true) + DeBuffLimitSheet deBuffLimitSheet, bool updateImmediate = true) { _buffStatModifiers.Clear(); if (!(value is null)) { foreach (var buff in value) { - AddBuff(buff, false); + AddBuff(buff, deBuffLimitSheet, false); } } @@ -322,9 +324,27 @@ public CharacterStats SetCollections(IEnumerable statModifiers, return this; } - public void AddBuff(Buff.StatBuff buff, bool updateImmediate = true) + public void AddBuff(Buff.StatBuff buff, DeBuffLimitSheet deBuffLimitSheet, bool updateImmediate = true) { - _buffStatModifiers[buff.RowData.GroupId] = buff.GetModifier(); + var modifier = buff.GetModifier(); + if (buff.IsDebuff()) + { + var statType = modifier.StatType; + var stat = _statMap.GetStatAsLong(statType); + var buffModified = modifier.GetModifiedValue(stat); + var row = deBuffLimitSheet.Values.FirstOrDefault(r => + r.Modifier.StatType == statType); + if (row is not null) + { + var limitModifier = row.Modifier; + var maxModified = (long)limitModifier.GetModifiedValue(stat); + if (maxModified > buffModified) + { + modifier = limitModifier; + } + } + } + _buffStatModifiers[buff.RowData.GroupId] = modifier; if (updateImmediate) { diff --git a/Lib9c/TableCSV/Skill/DeBuffLimitSheet.csv b/Lib9c/TableCSV/Skill/DeBuffLimitSheet.csv new file mode 100644 index 0000000000..64f554b3ae --- /dev/null +++ b/Lib9c/TableCSV/Skill/DeBuffLimitSheet.csv @@ -0,0 +1,6 @@ +id,stat_type,percentage +1,ATK,-1 +2,DEF,-10 +3,CRI,-20 +4,HIT,-50 +5,SPD,-100 diff --git a/Lib9c/TableData/Skill/DeBuffLimitSheet.cs b/Lib9c/TableData/Skill/DeBuffLimitSheet.cs new file mode 100644 index 0000000000..531f557e68 --- /dev/null +++ b/Lib9c/TableData/Skill/DeBuffLimitSheet.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using Nekoyume.Model.Stat; +using Org.BouncyCastle.Tls; +using static Nekoyume.TableData.TableExtensions; + +namespace Nekoyume.TableData +{ + public class DeBuffLimitSheet : Sheet + { + public class Row : SheetRow + { + public override int Key => Id; + + public int Id { get; set; } + public StatModifier Modifier { get; set; } + + public override void Set(IReadOnlyList fields) + { + Id = ParseInt(fields[0]); + Modifier = new StatModifier( + (StatType) Enum.Parse(typeof(StatType), fields[1]), + StatModifier.OperationType.Percentage, + ParseInt(fields[2]) + ); + } + } + + public DeBuffLimitSheet() : base(nameof(DeBuffLimitSheet)) + { + } + } +}