diff --git a/.Lib9c.Tests/Action/HitHelperTest.cs b/.Lib9c.Tests/Action/HitHelperTest.cs new file mode 100644 index 000000000..1b53168ff --- /dev/null +++ b/.Lib9c.Tests/Action/HitHelperTest.cs @@ -0,0 +1,34 @@ +namespace Lib9c.Tests.Action +{ + using System; + using Nekoyume.Battle; + using Xunit; + + public class HitHelperTest + { + [Fact] + public void GetHitStep2() + { + // copy from previous logic + int GetHitStep2Legacy(int attackerHit, int defenderHit) + { + attackerHit = Math.Max(1, attackerHit); + defenderHit = Math.Max(1, defenderHit); + var additionalCorrection = (int)((attackerHit - defenderHit / 3m) / defenderHit * 100); + return Math.Min( + Math.Max(additionalCorrection, HitHelper.GetHitStep2AdditionalCorrectionMin), + HitHelper.GetHitStep2AdditionalCorrectionMax); + } + + for (int i = 0; i < 9; i++) + { + for (int j = 0; j < 9; j++) + { + var legacy = GetHitStep2Legacy(i, j); + var current = HitHelper.GetHitStep2(i, j); + Assert.True(legacy == current, $"{i}, {j}, {legacy}, {current}"); + } + } + } + } +} diff --git a/.Lib9c.Tests/Model/Skill/CombatTest.cs b/.Lib9c.Tests/Model/Skill/CombatTest.cs index fb8f8c05f..162276101 100644 --- a/.Lib9c.Tests/Model/Skill/CombatTest.cs +++ b/.Lib9c.Tests/Model/Skill/CombatTest.cs @@ -75,7 +75,7 @@ public void CalculateDEFAndDamageReduction(int def, int drv, int drr, int enemyA var normalAttack = new NormalAttack(skillRow, 0, 100, default, StatType.NONE); var prevHP = _player.CurrentHP; - normalAttack.Use(_enemy, 1, new List()); + normalAttack.Use(_enemy, 1, new List(), false); var currentHP = _player.CurrentHP; var damage = prevHP - currentHP; @@ -100,11 +100,41 @@ public void CalculateCritDamage(int cdmg, int atk, int expectedDamage) var normalAttack = new NormalAttack(skillRow, 0, 100, default, StatType.NONE); var prevHP = _player.CurrentHP; - normalAttack.Use(_enemy, 1, new List()); + normalAttack.Use(_enemy, 1, new List(), false); var currentHP = _player.CurrentHP; var damage = prevHP - currentHP; Assert.Equal(expectedDamage, damage); } + + [Fact] + public void Thorn() + { + var prevHP = _enemy.CurrentHP; + var skill = _enemy.GiveThornDamage(1); + var currentHP = _enemy.CurrentHP; + // get 1dmg from thorn + Assert.Equal(prevHP - 1, currentHP); + Assert.Equal(prevHP, skill.Character.CurrentHP); + var skillInfo = Assert.Single(skill.SkillInfos); + Assert.Equal(currentHP, skillInfo.Target!.CurrentHP); + } + + [Fact] + public void Bleed() + { + var actionBuffSheet = _tableSheets.ActionBuffSheet; + var row = actionBuffSheet.Values.First(); + var bleed = Assert.IsType(BuffFactory.GetActionBuff(_enemy.Stats, row)); + var dmg = bleed.Power; + var prevHP = _player.CurrentHP; + var skill = bleed.GiveEffect(_player, 1); + var currentHP = _player.CurrentHP; + // get dmg from bleed + Assert.Equal(prevHP - dmg, currentHP); + Assert.Equal(prevHP, skill.Character.CurrentHP); + var skillInfo = Assert.Single(skill.SkillInfos); + Assert.Equal(currentHP, skillInfo.Target!.CurrentHP); + } } } diff --git a/.Lib9c.Tests/Model/Skill/NormalAttackTest.cs b/.Lib9c.Tests/Model/Skill/NormalAttackTest.cs index e6a8c843c..42e53cb77 100644 --- a/.Lib9c.Tests/Model/Skill/NormalAttackTest.cs +++ b/.Lib9c.Tests/Model/Skill/NormalAttackTest.cs @@ -26,8 +26,10 @@ public NormalAttackTest(ITestOutputHelper outputHelper) .CreateLogger(); } - [Fact] - public void Use() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void Use(bool copyCharacter) { var sheets = TableSheetsImporter.ImportSheets(); var tableSheets = new TableSheets(sheets); @@ -65,7 +67,8 @@ public void Use() StageSimulator.GetWaveRewards( random, tableSheets.StageSheet[1], - tableSheets.MaterialItemSheet) + tableSheets.MaterialItemSheet), + copyCharacter ); var player = new Player(avatarState, simulator); @@ -79,13 +82,13 @@ public void Use() var battleStatusSkill = normalAttack.Use( player, 0, - new List()); + new List(), + copyCharacter); Assert.NotNull(battleStatusSkill); - Assert.Single(battleStatusSkill.SkillInfos); - - var skillInfo = battleStatusSkill.SkillInfos.FirstOrDefault(); - Assert.NotNull(skillInfo); - Assert.Equal(enemy.Id, skillInfo.Target.Id); + Assert.Equal(!copyCharacter, battleStatusSkill.Character is null); + var skillInfo = Assert.Single(battleStatusSkill.SkillInfos); + Assert.Equal(enemy.Id, skillInfo.CharacterId); + Assert.Equal(!copyCharacter, skillInfo.Target is null); } } } diff --git a/Lib9c/Action/HackAndSlash.cs b/Lib9c/Action/HackAndSlash.cs index c25ee7faa..926ee68fc 100644 --- a/Lib9c/Action/HackAndSlash.cs +++ b/Lib9c/Action/HackAndSlash.cs @@ -439,8 +439,14 @@ public IAccountStateDelta Execute( } } + var stageWaveRow = sheets.GetSheet()[StageId]; + var enemySkillSheet = sheets.GetSheet(); + var costumeStatSheet = sheets.GetSheet(); + var stageCleared = !isNotClearedStage; + var starCount = 0; for (var i = 0; i < TotalPlayCount; i++) { + var rewards = StageSimulator.GetWaveRewards(random, stageRow, materialItemSheet); sw.Restart(); // First simulating will use Foods and Random Skills. // Remainder simulating will not use Foods. @@ -453,13 +459,14 @@ public IAccountStateDelta Execute( WorldId, StageId, stageRow, - sheets.GetSheet()[StageId], - avatarState.worldInformation.IsStageCleared(StageId), + stageWaveRow, + stageCleared, StageRewardExpHelper.GetExp(avatarState.level, StageId), simulatorSheets, - sheets.GetSheet(), - sheets.GetSheet(), - StageSimulator.GetWaveRewards(random, stageRow, materialItemSheet)); + enemySkillSheet, + costumeStatSheet, + rewards, + false); sw.Stop(); Log.Verbose("{AddressesHex}HAS Initialize Simulator: {Elapsed}", addressesHex, sw.Elapsed); @@ -471,13 +478,17 @@ public IAccountStateDelta Execute( sw.Restart(); if (simulator.Log.IsClear) { - simulator.Player.worldInformation.ClearStage( - WorldId, - StageId, - blockIndex, - worldSheet, - worldUnlockSheet - ); + if (!stageCleared) + { + avatarState.worldInformation.ClearStage( + WorldId, + StageId, + blockIndex, + worldSheet, + worldUnlockSheet + ); + stageCleared = true; + } sw.Stop(); Log.Verbose("{AddressesHex}HAS ClearStage: {Elapsed}", addressesHex, sw.Elapsed); } @@ -504,9 +515,8 @@ public IAccountStateDelta Execute( player.eventMapForBeforeV100310.Clear(); } + starCount += simulator.Log.clearedWaveNumber; avatarState.Update(simulator); - // Update CrystalRandomSkillState.Stars by clearedWaveNumber. (add) - skillState?.Update(simulator.Log.clearedWaveNumber, crystalStageBuffSheet); sw.Stop(); Log.Verbose( @@ -526,6 +536,8 @@ public IAccountStateDelta Execute( Log.Verbose("{AddressesHex}HAS loop Simulate: {Elapsed}, Count: {PlayCount}", addressesHex, sw.Elapsed, TotalPlayCount); + // Update CrystalRandomSkillState.Stars by clearedWaveNumber. (add) + skillState?.Update(starCount, crystalStageBuffSheet); sw.Restart(); avatarState.UpdateQuestRewards(materialItemSheet); avatarState.updatedAt = blockIndex; diff --git a/Lib9c/Battle/AttackCountHelper.cs b/Lib9c/Battle/AttackCountHelper.cs index 89dc520f9..7fffe6ff7 100644 --- a/Lib9c/Battle/AttackCountHelper.cs +++ b/Lib9c/Battle/AttackCountHelper.cs @@ -1,68 +1,14 @@ using System; using System.Collections.Generic; +using System.Diagnostics; namespace Nekoyume.Battle { public static class AttackCountHelper { - public struct Info - { - public decimal DamageMultiplier; - public decimal AdditionalCriticalChance; - } - public const int CountMaxLowerLimit = 2; public const int CountMaxUpperLimit = 5; - /// - /// key: attack count max - /// value: attack count, info - /// - public static readonly IReadOnlyDictionary> CachedInfo = - new Dictionary> - { - { - 1, new Dictionary - { - {1, new Info {DamageMultiplier = 1m, AdditionalCriticalChance = 0m}} - } - }, - { - 2, new Dictionary - { - {1, new Info {DamageMultiplier = 1m, AdditionalCriticalChance = 0m}}, - {2, new Info {DamageMultiplier = 2m, AdditionalCriticalChance = 25m}} - } - }, - { - 3, new Dictionary - { - {1, new Info {DamageMultiplier = 1m, AdditionalCriticalChance = 0m}}, - {2, new Info {DamageMultiplier = 2m, AdditionalCriticalChance = 10m}}, - {3, new Info {DamageMultiplier = 3m, AdditionalCriticalChance = 35m}} - } - }, - { - 4, new Dictionary - { - {1, new Info {DamageMultiplier = 1m, AdditionalCriticalChance = 0m}}, - {2, new Info {DamageMultiplier = 2m, AdditionalCriticalChance = 10m}}, - {3, new Info {DamageMultiplier = 3m, AdditionalCriticalChance = 20m}}, - {4, new Info {DamageMultiplier = 4m, AdditionalCriticalChance = 45m}} - } - }, - { - 5, new Dictionary - { - {1, new Info {DamageMultiplier = 1m, AdditionalCriticalChance = 0m}}, - {2, new Info {DamageMultiplier = 2m, AdditionalCriticalChance = 10m}}, - {3, new Info {DamageMultiplier = 3m, AdditionalCriticalChance = 20m}}, - {4, new Info {DamageMultiplier = 4m, AdditionalCriticalChance = 30m}}, - {5, new Info {DamageMultiplier = 5m, AdditionalCriticalChance = 55m}} - } - } - }; - public static int GetCountMax(int level) { if (level < 11) @@ -76,40 +22,69 @@ public static int GetCountMax(int level) : CountMaxUpperLimit; } - public static decimal GetDamageMultiplier(int attackCount, int attackCountMax) + public static int GetDamageMultiplier(int attackCount, int attackCountMax) { if (attackCount > attackCountMax) throw new ArgumentOutOfRangeException( $"{nameof(attackCount)}: {attackCount} / {nameof(attackCountMax)}: {attackCountMax}"); - var info = GetInfo(attackCount, attackCountMax); - return info.DamageMultiplier; - } - - public static decimal GetAdditionalCriticalChance(int attackCount, int attackCountMax) - { - if (attackCount > attackCountMax) - throw new ArgumentOutOfRangeException( - $"{nameof(attackCount)}: {attackCount} / {nameof(attackCountMax)}: {attackCountMax}"); + if (attackCountMax <= 5) + { + return Math.Max(1, attackCount); + } - var info = GetInfo(attackCount, attackCountMax); - return info.AdditionalCriticalChance; + throw new ArgumentOutOfRangeException(); } - private static Info GetInfo(int attackCount, int attackCountMax) + public static int GetAdditionalCriticalChance(int attackCount, int attackCountMax) { if (attackCount > attackCountMax) throw new ArgumentOutOfRangeException( $"{nameof(attackCount)}: {attackCount} / {nameof(attackCountMax)}: {attackCountMax}"); + switch (attackCount) + { + case 1: + return 0; + case 2: + switch (attackCountMax) + { + case 2: + return 25; + case 3: + case 4: + case 5: + return 10; + } + break; + case 3: + switch (attackCountMax) + { + case 3: + return 35; + case 4: + case 5: + return 20; + } + break; + case 4: + switch (attackCountMax) + { + case 4: + return 45; + case 5: + return 30; + } + break; + case 5: + switch (attackCountMax) + { + case 5: + return 55; + } + break; + } - if (!CachedInfo.ContainsKey(attackCountMax)) - throw new ArgumentOutOfRangeException($"{nameof(attackCountMax)}: {attackCountMax}"); - - if (!CachedInfo[attackCountMax].ContainsKey(attackCount)) - throw new ArgumentOutOfRangeException( - $"{nameof(attackCountMax)}: {attackCountMax} / {nameof(attackCount)}: {attackCount}"); - - return CachedInfo[attackCountMax][attackCount]; + throw new ArgumentOutOfRangeException(); } } } diff --git a/Lib9c/Battle/HitHelper.cs b/Lib9c/Battle/HitHelper.cs index f2d9c2caa..49cb4932f 100644 --- a/Lib9c/Battle/HitHelper.cs +++ b/Lib9c/Battle/HitHelper.cs @@ -64,101 +64,52 @@ public static bool IsHitWithoutLevelCorrection( public static int GetHitStep1(int attackerLevel, int defenderLevel) { - var correction = 0; var diff = attackerLevel - defenderLevel; if (diff <= GetHitStep1LevelDiffMin) { - correction = GetHitStep1CorrectionMin; + return GetHitStep1CorrectionMin; } - else if (diff >= GetHitStep1LevelDiffMax) - { - correction = GetHitStep1CorrectionMax; - } - else + + if (diff >= GetHitStep1LevelDiffMax) { - switch (diff) - { - case -13: - correction = -4; - break; - case -12: - correction = -3; - break; - case -11: - correction = -2; - break; - case -10: - correction = -1; - break; - case -9: - correction = 0; - break; - case -8: - correction = 1; - break; - case -7: - correction = 2; - break; - case -6: - correction = 4; - break; - case -5: - correction = 6; - break; - case -4: - correction = 8; - break; - case -3: - correction = 13; - break; - case -2: - correction = 20; - break; - case -1: - correction = 28; - break; - case 0: - correction = 40; - break; - case 1: - correction = 41; - break; - case 2: - correction = 42; - break; - case 3: - correction = 43; - break; - case 4: - correction = 44; - break; - case 5: - correction = 45; - break; - case 6: - correction = 46; - break; - case 7: - correction = 47; - break; - case 8: - correction = 48; - break; - case 9: - correction = 49; - break; - } + return GetHitStep1CorrectionMax; } - return correction; + return diff switch + { + -13 => -4, + -12 => -3, + -11 => -2, + -10 => -1, + -9 => 0, + -8 => 1, + -7 => 2, + -6 => 4, + -5 => 6, + -4 => 8, + -3 => 13, + -2 => 20, + -1 => 28, + 0 => 40, + 1 => 41, + 2 => 42, + 3 => 43, + 4 => 44, + 5 => 45, + 6 => 46, + 7 => 47, + 8 => 48, + 9 => 49, + _ => 0 + }; } public static int GetHitStep2(int attackerHit, int defenderHit) { attackerHit = Math.Max(1, attackerHit); defenderHit = Math.Max(1, defenderHit); - var additionalCorrection = (int) ((attackerHit - defenderHit / 3m) / defenderHit * 100); + var additionalCorrection = (attackerHit * 10000 - defenderHit * 10000 / 3) / defenderHit / 100; return Math.Min(Math.Max(additionalCorrection, GetHitStep2AdditionalCorrectionMin), GetHitStep2AdditionalCorrectionMax); } diff --git a/Lib9c/Battle/ISimulator.cs b/Lib9c/Battle/ISimulator.cs index d68c70b41..00f45d4ee 100644 --- a/Lib9c/Battle/ISimulator.cs +++ b/Lib9c/Battle/ISimulator.cs @@ -14,5 +14,6 @@ public interface ISimulator IEnumerable Reward { get; } int WaveNumber { get; } int WaveTurn { get; } + bool LogEvent { get; } } } diff --git a/Lib9c/Battle/RaidBoss.cs b/Lib9c/Battle/RaidBoss.cs index 3ab518802..ce8b9231d 100644 --- a/Lib9c/Battle/RaidBoss.cs +++ b/Lib9c/Battle/RaidBoss.cs @@ -104,7 +104,8 @@ protected override BattleStatus.Skill UseSkill() Simulator.StatBuffSheet, Simulator.SkillActionBuffSheet, Simulator.ActionBuffSheet - ) + ), + Simulator.LogEvent ); Simulator.Log.Add(usedSkill); @@ -145,16 +146,17 @@ public void Enrage() Simulator.StatBuffSheet, Simulator.SkillActionBuffSheet, Simulator.ActionBuffSheet - ) + ), + Simulator.LogEvent ); Simulator.Log.Add(usedSkill); foreach (var info in usedSkill.SkillInfos) { - if (!info.Target.IsDead) + if (!info.IsDead) continue; - var target = Targets.FirstOrDefault(i => i.Id == info.Target.Id); + var target = Targets.FirstOrDefault(i => i.Id == info.CharacterId); target?.Die(); } } diff --git a/Lib9c/Battle/Simulator.cs b/Lib9c/Battle/Simulator.cs index 70b4d69c6..7d43a46bc 100644 --- a/Lib9c/Battle/Simulator.cs +++ b/Lib9c/Battle/Simulator.cs @@ -33,14 +33,14 @@ public abstract class Simulator : ISimulator public int WaveNumber { get; protected set; } public int WaveTurn { get; set; } public abstract IEnumerable Reward { get; } + public bool LogEvent { get; protected set; } - protected Simulator( - IRandom random, + protected Simulator(IRandom random, AvatarState avatarState, List foods, - SimulatorSheetsV1 simulatorSheets - ) : this(random, new Player(avatarState, simulatorSheets), foods, simulatorSheets) + SimulatorSheetsV1 simulatorSheets, bool logEvent = true) : this(random, new Player(avatarState, simulatorSheets), foods, simulatorSheets) { + LogEvent = logEvent; } protected Simulator( diff --git a/Lib9c/Battle/StageSimulator.cs b/Lib9c/Battle/StageSimulator.cs index 16b43b70b..e01122b6b 100644 --- a/Lib9c/Battle/StageSimulator.cs +++ b/Lib9c/Battle/StageSimulator.cs @@ -13,6 +13,7 @@ using Nekoyume.Model.Buff; using Nekoyume.TableData; using Priority_Queue; +using Skill = Nekoyume.Model.Skill.Skill; namespace Nekoyume.Battle { @@ -32,12 +33,11 @@ public class StageSimulator : Simulator, IStageSimulator private int TurnLimit { get; } public override IEnumerable Reward => _waveRewards; - public StageSimulator( - IRandom random, + public StageSimulator(IRandom random, AvatarState avatarState, List foods, List runeStates, - List skillsOnWaveStart, + List skillsOnWaveStart, int worldId, int stageId, StageSheet.Row stageRow, @@ -47,12 +47,14 @@ public StageSimulator( SimulatorSheets simulatorSheets, EnemySkillSheet enemySkillSheet, CostumeStatSheet costumeStatSheet, - List waveRewards) + List waveRewards, + bool logEvent = true) : base( random, avatarState, foods, - simulatorSheets) + simulatorSheets, + logEvent) { Player.SetCostumeStat(costumeStatSheet); if (runeStates != null) @@ -128,8 +130,7 @@ public Player Simulate() ActionBuffSheet ); - var usedSkill = skill.Use(Player, 0, buffs); - Log.Add(usedSkill); + skill.Use(Player, 0, buffs, LogEvent); } while (true) @@ -142,7 +143,7 @@ public Player Simulate() Result = BattleLog.Result.Lose; if (Exp > 0) { - Player.GetExp((int)(Exp * 0.3m), true); + Player.GetExp((int)(Exp * 0.3m), LogEvent); } } else @@ -169,7 +170,7 @@ public Player Simulate() Result = BattleLog.Result.Lose; if (Exp > 0) { - Player.GetExp((int)(Exp * 0.3m), true); + Player.GetExp((int)(Exp * 0.3m), LogEvent); } } else @@ -192,7 +193,7 @@ public Player Simulate() { if (Exp > 0) { - Player.GetExp(Exp, true); + Player.GetExp(Exp, LogEvent); } break; @@ -200,10 +201,13 @@ public Player Simulate() case 2: { ItemMap = Player.GetRewards(_waveRewards); - var dropBox = new DropBox(null, _waveRewards); - Log.Add(dropBox); - var getReward = new GetReward(null, _waveRewards); - Log.Add(getReward); + if (LogEvent) + { + var dropBox = new DropBox(null, _waveRewards); + Log.Add(dropBox); + var getReward = new GetReward(null, _waveRewards); + Log.Add(getReward); + } break; } default: diff --git a/Lib9c/Battle/StageSimulatorV1.cs b/Lib9c/Battle/StageSimulatorV1.cs index 66e386f25..bafcaf03e 100644 --- a/Lib9c/Battle/StageSimulatorV1.cs +++ b/Lib9c/Battle/StageSimulatorV1.cs @@ -301,7 +301,7 @@ CostumeStatSheet costumeStatSheet { Player.SetCostumeStat(costumeStatSheet); } - + public Player Simulate(int playCount) { Log.worldId = WorldId; @@ -331,7 +331,7 @@ public Player Simulate(int playCount) ActionBuffSheet ); - var usedSkill = skill.Use(Player, 0, buffs); + var usedSkill = skill.Use(Player, 0, buffs, LogEvent); Log.Add(usedSkill); } diff --git a/Lib9c/Battle/StageSimulatorV2.cs b/Lib9c/Battle/StageSimulatorV2.cs index 12845f69e..08aed9dc7 100644 --- a/Lib9c/Battle/StageSimulatorV2.cs +++ b/Lib9c/Battle/StageSimulatorV2.cs @@ -122,7 +122,7 @@ public Player Simulate() ActionBuffSheet ); - var usedSkill = skill.Use(Player, 0, buffs); + var usedSkill = skill.Use(Player, 0, buffs, LogEvent); Log.Add(usedSkill); } diff --git a/Lib9c/Battle/StageSimulatorV3.cs b/Lib9c/Battle/StageSimulatorV3.cs index 253327ea3..5d47a6248 100644 --- a/Lib9c/Battle/StageSimulatorV3.cs +++ b/Lib9c/Battle/StageSimulatorV3.cs @@ -128,7 +128,7 @@ public Player Simulate() ActionBuffSheet ); - var usedSkill = skill.Use(Player, 0, buffs); + var usedSkill = skill.Use(Player, 0, buffs, LogEvent); Log.Add(usedSkill); } diff --git a/Lib9c/Battle/Wave.cs b/Lib9c/Battle/Wave.cs index a938356c1..99bd2faf1 100644 --- a/Lib9c/Battle/Wave.cs +++ b/Lib9c/Battle/Wave.cs @@ -25,9 +25,12 @@ public void Spawn(ISimulator simulator) enemy.InitAI(); } - var enemies = _enemies.Select(enemy => new Enemy(enemy)).ToList(); - var spawnWave = new SpawnWave(null, simulator.WaveNumber, simulator.WaveTurn, enemies, HasBoss); - simulator.Log.Add(spawnWave); + if (simulator.LogEvent) + { + var enemies = _enemies.Select(enemy => new Enemy(enemy)).ToList(); + var spawnWave = new SpawnWave(null, simulator.WaveNumber, simulator.WaveTurn, enemies, HasBoss); + simulator.Log.Add(spawnWave); + } } [Obsolete("Use Spawn")] diff --git a/Lib9c/Model/BattleStatus/Skill.cs b/Lib9c/Model/BattleStatus/Skill.cs index 3c7f3bcf2..48149d5a4 100644 --- a/Lib9c/Model/BattleStatus/Skill.cs +++ b/Lib9c/Model/BattleStatus/Skill.cs @@ -12,22 +12,27 @@ public abstract class Skill : EventBase [Serializable] public class SkillInfo { - public readonly CharacterBase Target; + public readonly CharacterBase? Target; public readonly int Effect; public readonly bool Critical; public readonly SkillCategory SkillCategory; public readonly ElementalType ElementalType; public readonly SkillTargetType SkillTargetType; public readonly int WaveTurn; + public readonly int Thorn; + public readonly bool IsDead; + public readonly Guid CharacterId; + - public readonly Model.Buff.Buff? Buff; - public SkillInfo(CharacterBase character, int effect, bool critical, SkillCategory skillCategory, + public SkillInfo(Guid characterId, bool isDead, int thorn, int effect, bool critical, SkillCategory skillCategory, int waveTurn, ElementalType elementalType = ElementalType.Normal, - SkillTargetType targetType = SkillTargetType.Enemy, Model.Buff.Buff? buff = null) + SkillTargetType targetType = SkillTargetType.Enemy, Model.Buff.Buff? buff = null, CharacterBase? target = null) { - Target = character; + CharacterId = characterId; + IsDead = isDead; + Thorn = thorn; Effect = effect; Critical = critical; SkillCategory = skillCategory; @@ -35,6 +40,7 @@ public SkillInfo(CharacterBase character, int effect, bool critical, SkillCatego SkillTargetType = targetType; Buff = buff; WaveTurn = waveTurn; + Target = target; } } @@ -42,7 +48,7 @@ public SkillInfo(CharacterBase character, int effect, bool critical, SkillCatego public readonly IEnumerable SkillInfos; - + public readonly IEnumerable? BuffInfos; protected Skill(int skillId, CharacterBase character, IEnumerable skillInfos, diff --git a/Lib9c/Model/Buff/Bleed.cs b/Lib9c/Model/Buff/Bleed.cs index 501170351..800f9bd77 100644 --- a/Lib9c/Model/Buff/Bleed.cs +++ b/Lib9c/Model/Buff/Bleed.cs @@ -41,9 +41,10 @@ public override BattleStatus.Skill GiveEffect( var damageInfos = new List { - new BattleStatus.Skill.SkillInfo((CharacterBase)affectedCharacter.Clone(), damage, false, + // Copy new Character with damaged. + new BattleStatus.Skill.SkillInfo(affectedCharacter.Id, affectedCharacter.IsDead, affectedCharacter.Thorn, damage, false, SkillCategory.Debuff, simulatorWaveTurn, RowData.ElementalType, - RowData.TargetType) + RowData.TargetType, target: (CharacterBase)affectedCharacter.Clone()) }; return new Model.BattleStatus.TickDamage( diff --git a/Lib9c/Model/Character/ArenaCharacter.cs b/Lib9c/Model/Character/ArenaCharacter.cs index 2ec210f0d..d487336ea 100644 --- a/Lib9c/Model/Character/ArenaCharacter.cs +++ b/Lib9c/Model/Character/ArenaCharacter.cs @@ -819,7 +819,7 @@ public bool IsCritical(bool considerAttackCount = true) return CRI >= chance; var additionalCriticalChance = - (int) AttackCountHelper.GetAdditionalCriticalChance(_attackCount, _attackCountMax); + AttackCountHelper.GetAdditionalCriticalChance(_attackCount, _attackCountMax); return CRI + additionalCriticalChance >= chance; } @@ -850,7 +850,7 @@ public int GetDamage(int damage, bool considerAttackCount = true) _attackCount = 1; } - var damageMultiplier = (int) AttackCountHelper.GetDamageMultiplier(_attackCount, _attackCountMax); + var damageMultiplier = AttackCountHelper.GetDamageMultiplier(_attackCount, _attackCountMax); damage *= damageMultiplier; return damage; } diff --git a/Lib9c/Model/Character/CharacterBase.cs b/Lib9c/Model/Character/CharacterBase.cs index b338688b7..e60c08517 100644 --- a/Lib9c/Model/Character/CharacterBase.cs +++ b/Lib9c/Model/Character/CharacterBase.cs @@ -225,7 +225,7 @@ private void ReduceDurationOfBuffs() pair.Value.RemainedDuration--; } } - + protected virtual void ReduceSkillCooldown() { Skills.ReduceCooldown(); @@ -240,6 +240,7 @@ private void ReduceSkillCooldownV1() protected virtual BattleStatus.Skill UseSkill() { var selectedSkill = Skills.Select(Simulator.Random); + bool log = Simulator.LogEvent; var usedSkill = selectedSkill.Use( this, Simulator.WaveTurn, @@ -250,7 +251,8 @@ protected virtual BattleStatus.Skill UseSkill() Simulator.StatBuffSheet, Simulator.SkillActionBuffSheet, Simulator.ActionBuffSheet - ) + ), + log ); if (!Simulator.SkillSheet.TryGetValue(selectedSkill.SkillRow.Id, out var sheetSkill)) @@ -259,7 +261,10 @@ protected virtual BattleStatus.Skill UseSkill() } Skills.SetCooldown(selectedSkill.SkillRow.Id, sheetSkill.Cooldown); - Simulator.Log.Add(usedSkill); + if (log) + { + Simulator.Log.Add(usedSkill); + } return usedSkill; } @@ -278,7 +283,8 @@ private BattleStatus.Skill UseSkillV1() Simulator.StatBuffSheet, Simulator.SkillActionBuffSheet, Simulator.ActionBuffSheet - ) + ), + Simulator.LogEvent ); Skills.SetCooldown(selectedSkill.SkillRow.Id, selectedSkill.SkillRow.Cooldown); @@ -301,7 +307,8 @@ private BattleStatus.Skill UseSkillV2() Simulator.StatBuffSheet, Simulator.SkillActionBuffSheet, Simulator.ActionBuffSheet - ) + ), + Simulator.LogEvent ); Skills.SetCooldown(selectedSkill.SkillRow.Id, selectedSkill.SkillRow.Cooldown); @@ -328,7 +335,10 @@ private void RemoveBuffs() return; Stats.SetBuffs(StatBuffs); - Simulator.Log.Add(new RemoveBuffs((CharacterBase) Clone())); + if (Simulator.LogEvent) + { + Simulator.Log.Add(new RemoveBuffs((CharacterBase) Clone())); + } } protected virtual void EndTurn() @@ -410,7 +420,7 @@ public bool IsCritical(bool considerAttackCount = true) return CRI >= chance; var additionalCriticalChance = - (int) AttackCountHelper.GetAdditionalCriticalChance(AttackCount, AttackCountMax); + AttackCountHelper.GetAdditionalCriticalChance(AttackCount, AttackCountMax); return CRI + additionalCriticalChance >= chance; } @@ -443,7 +453,7 @@ public int GetDamage(int damage, bool considerAttackCount = true) AttackCount = 1; } - var damageMultiplier = (int) AttackCountHelper.GetDamageMultiplier(AttackCount, AttackCountMax); + var damageMultiplier = AttackCountHelper.GetDamageMultiplier(AttackCount, AttackCountMax); damage *= damageMultiplier; #if TEST_LOG @@ -465,8 +475,11 @@ public void Die() protected virtual void OnDead() { - var dead = new Dead((CharacterBase) Clone()); - Simulator.Log.Add(dead); + if (Simulator.LogEvent) + { + var dead = new Dead((CharacterBase) Clone()); + Simulator.Log.Add(dead); + } } public void Heal(int heal) @@ -551,10 +564,14 @@ protected virtual void OnPreSkill() protected virtual void OnPostSkill(BattleStatus.Skill usedSkill) { var bleeds = Buffs.Values.OfType().OrderBy(x => x.BuffInfo.Id); + bool log = Simulator.LogEvent; foreach (var bleed in bleeds) { var effect = bleed.GiveEffect(this, Simulator.WaveTurn); - Simulator.Log.Add(effect); + if (log) + { + Simulator.Log.Add(effect); + } } // Apply thorn damage if target has thorn @@ -566,10 +583,13 @@ protected virtual void OnPostSkill(BattleStatus.Skill usedSkill) skillInfo.SkillCategory == SkillCategory.DoubleAttack || skillInfo.SkillCategory == SkillCategory.AreaAttack || skillInfo.SkillCategory == SkillCategory.BuffRemovalAttack; - if (isAttackSkill && skillInfo.Target.Thorn > 0) + if (isAttackSkill && skillInfo.Thorn > 0) { - var effect = GiveThornDamage(skillInfo.Target.Thorn); - Simulator.Log.Add(effect); + var effect = GiveThornDamage(skillInfo.Thorn); + if (log) + { + Simulator.Log.Add(effect); + } } } @@ -582,31 +602,38 @@ protected virtual void OnPostSkill(BattleStatus.Skill usedSkill) FinishTargetIfKilled(usedSkill); } - private BattleStatus.Skill GiveThornDamage(int targetThorn) + internal BattleStatus.Skill GiveThornDamage(int targetThorn) { - var clone = (CharacterBase)Clone(); + bool log = Simulator.LogEvent; + // Copy not damaged character + var clone = log ? (CharacterBase)Clone() : null; // minimum 1 damage var thornDamage = Math.Max(1, targetThorn - DEF); CurrentHP -= thornDamage; - var damageInfos = new List() + if (log) { - new BattleStatus.Skill.SkillInfo( - (CharacterBase)Clone(), - thornDamage, - false, - SkillCategory.TickDamage, - Simulator.WaveTurn, - ElementalType.Normal, - SkillTargetType.Enemy) - }; - - var tickDamage = new TickDamage( - default, - clone, - damageInfos, - null); + // Copy new damaged character + var damageInfos = new List() + { + new BattleStatus.Skill.SkillInfo( + Id, + IsDead, + thornDamage, + thornDamage, + false, + SkillCategory.TickDamage, + Simulator.WaveTurn, + target: (CharacterBase)Clone()) + }; + var tickDamage = new TickDamage( + default, + clone, + damageInfos, + null); + return tickDamage; + } - return tickDamage; + return null; } private void FinishTargetIfKilledForBeforeV100310(BattleStatus.Skill usedSkill) @@ -614,7 +641,7 @@ private void FinishTargetIfKilledForBeforeV100310(BattleStatus.Skill usedSkill) var isFirst = true; foreach (var info in usedSkill.SkillInfos) { - if (!info.Target.IsDead) + if (!info.IsDead) { continue; } @@ -626,7 +653,7 @@ private void FinishTargetIfKilledForBeforeV100310(BattleStatus.Skill usedSkill) } var target = Targets.FirstOrDefault(i => - i.Id == info.Target.Id); + i.Id == info.CharacterId); switch (target) { case Player player: @@ -655,12 +682,12 @@ private void FinishTargetIfKilled(BattleStatus.Skill usedSkill) var killedTargets = new List(); foreach (var info in usedSkill.SkillInfos) { - if (!info.Target.IsDead) + if (!info.IsDead) { continue; } - var target = Targets.FirstOrDefault(i => i.Id == info.Target.Id); + var target = Targets.FirstOrDefault(i => i.Id == info.CharacterId); if (!killedTargets.Contains(target)) { killedTargets.Add(target); diff --git a/Lib9c/Model/Character/Player.cs b/Lib9c/Model/Character/Player.cs index 44abc2ee4..f66bda838 100644 --- a/Lib9c/Model/Character/Player.cs +++ b/Lib9c/Model/Character/Player.cs @@ -449,8 +449,11 @@ public CollectionMap GetRewards2(List items) public virtual void Spawn() { InitAI(); - var spawn = new SpawnPlayer((CharacterBase)Clone()); - Simulator.Log.Add(spawn); + if (Simulator.LogEvent) + { + var spawn = new SpawnPlayer((CharacterBase)Clone()); + Simulator.Log.Add(spawn); + } } [Obsolete("Use Spawn")] @@ -512,6 +515,8 @@ protected override BattleStatus.Skill UseSkill() return base.UseSkill(); } + bool log = Simulator.LogEvent; + var usedSkill = selectedSkill.Use( this, Simulator.WaveTurn, @@ -522,12 +527,16 @@ protected override BattleStatus.Skill UseSkill() Simulator.StatBuffSheet, Simulator.SkillActionBuffSheet, Simulator.ActionBuffSheet - ) + ), + log ); var cooldown = RuneSkillCooldownMap[selectedSkill.SkillRow.Id]; RuneSkills.SetCooldown(selectedSkill.SkillRow.Id, cooldown); - Simulator.Log.Add(usedSkill); + if (log) + { + Simulator.Log.Add(usedSkill); + } return usedSkill; } @@ -672,7 +681,10 @@ protected override void EndTurn() Simulator.TurnNumber++; Simulator.WaveTurn++; - Simulator.Log.Add(new WaveTurnEnd(this, Simulator.TurnNumber, Simulator.WaveTurn)); + if (Simulator.LogEvent) + { + Simulator.Log.Add(new WaveTurnEnd(this, Simulator.TurnNumber, Simulator.WaveTurn)); + } } } } diff --git a/Lib9c/Model/Quest/CollectQuest.cs b/Lib9c/Model/Quest/CollectQuest.cs index ed00acf40..7f0b550a6 100644 --- a/Lib9c/Model/Quest/CollectQuest.cs +++ b/Lib9c/Model/Quest/CollectQuest.cs @@ -14,7 +14,7 @@ public class CollectQuest : Quest public readonly int ItemId; - public CollectQuest(CollectQuestSheet.Row data, QuestReward reward) + public CollectQuest(CollectQuestSheet.Row data, QuestReward reward) : base(data, reward) { ItemId = data.ItemId; @@ -54,11 +54,7 @@ public void Update(CollectionMap itemMap) } public override IValue Serialize() => -#pragma warning disable LAA1002 - new Dictionary(new Dictionary - { - [(Text)"itemId"] = (Integer)ItemId, - }.Union((Dictionary)base.Serialize())); -#pragma warning restore LAA1002 + ((Dictionary) base.Serialize()) + .Add("itemId", ItemId); } } diff --git a/Lib9c/Model/Quest/CombinationEquipmentQuest.cs b/Lib9c/Model/Quest/CombinationEquipmentQuest.cs index 405840357..6868333bb 100644 --- a/Lib9c/Model/Quest/CombinationEquipmentQuest.cs +++ b/Lib9c/Model/Quest/CombinationEquipmentQuest.cs @@ -58,14 +58,9 @@ public void Update(int recipeId) public override IValue Serialize() { - var dict = new Dictionary - { - [(Text) "recipe_id"] = RecipeId.Serialize(), - [(Text) "stage_id"] = StageId.Serialize(), - }; -#pragma warning disable LAA1002 - return new Dictionary(dict.Union((Dictionary) base.Serialize())); -#pragma warning restore LAA1002 + return ((Dictionary) base.Serialize()) + .Add("recipe_id", RecipeId.Serialize()) + .Add("stage_id", StageId.Serialize()); } } } diff --git a/Lib9c/Model/Quest/CombinationQuest.cs b/Lib9c/Model/Quest/CombinationQuest.cs index 236e5a8ab..fbe9bbbc7 100644 --- a/Lib9c/Model/Quest/CombinationQuest.cs +++ b/Lib9c/Model/Quest/CombinationQuest.cs @@ -16,7 +16,7 @@ public class CombinationQuest : Quest public override QuestType QuestType => QuestType.Craft; - public CombinationQuest(CombinationQuestSheet.Row data, QuestReward reward) + public CombinationQuest(CombinationQuestSheet.Row data, QuestReward reward) : base(data, reward) { ItemType = data.ItemType; @@ -58,13 +58,8 @@ public void Update(List items) } public override IValue Serialize() => -#pragma warning disable LAA1002 - new Dictionary(new Dictionary - { - [(Text)"itemType"] = (Integer)(int)ItemType, - [(Text)"itemSubType"] = (Integer)(int)ItemSubType, - }.Union((Dictionary)base.Serialize())); -#pragma warning restore LAA1002 - + ((Dictionary) base.Serialize()) + .Add("itemType", (int) ItemType) + .Add("itemSubType", (int) ItemSubType); } } diff --git a/Lib9c/Model/Quest/GeneralQuest.cs b/Lib9c/Model/Quest/GeneralQuest.cs index 466b532a9..f8f26999c 100644 --- a/Lib9c/Model/Quest/GeneralQuest.cs +++ b/Lib9c/Model/Quest/GeneralQuest.cs @@ -74,12 +74,8 @@ public void Update(CollectionMap eventMap) } public override IValue Serialize() => -#pragma warning disable LAA1002 - new Dictionary(new Dictionary - { - [(Text)"event"] = (Integer)(int)Event, - }.Union((Dictionary)base.Serialize())); -#pragma warning restore LAA1002 + ((Dictionary) base.Serialize()) + .Add("event", (int) Event); } } diff --git a/Lib9c/Model/Quest/GoldQuest.cs b/Lib9c/Model/Quest/GoldQuest.cs index 8bdba842d..890622a35 100644 --- a/Lib9c/Model/Quest/GoldQuest.cs +++ b/Lib9c/Model/Quest/GoldQuest.cs @@ -62,12 +62,7 @@ public void Update(FungibleAssetValue gold) } public override IValue Serialize() => -#pragma warning disable LAA1002 - new Dictionary(new Dictionary - { - [(Text)"type"] = (Integer)(int)Type, - }.Union((Dictionary)base.Serialize())); -#pragma warning restore LAA1002 - + ((Dictionary) base.Serialize()) + .Add("type", (int) Type); } } diff --git a/Lib9c/Model/Quest/ItemEnhancementQuest.cs b/Lib9c/Model/Quest/ItemEnhancementQuest.cs index 926bc1cb2..b93a56f24 100644 --- a/Lib9c/Model/Quest/ItemEnhancementQuest.cs +++ b/Lib9c/Model/Quest/ItemEnhancementQuest.cs @@ -16,7 +16,7 @@ public class ItemEnhancementQuest : Quest public int Count => _count; public override float Progress => (float) _current / _count; - public ItemEnhancementQuest(ItemEnhancementQuestSheet.Row data, QuestReward reward) + public ItemEnhancementQuest(ItemEnhancementQuestSheet.Row data, QuestReward reward) : base(data, reward) { _count = data.Count; @@ -63,13 +63,8 @@ public void Update(Equipment equipment) protected override string TypeId => "itemEnhancementQuest"; public override IValue Serialize() => -#pragma warning disable LAA1002 - new Dictionary(new Dictionary - { - [(Text)"grade"] = (Integer)Grade, - [(Text)"count"] = (Integer)_count, - }.Union((Dictionary)base.Serialize())); -#pragma warning restore LAA1002 - + ((Dictionary) base.Serialize()) + .Add("grade", Grade) + .Add("count", _count); } } diff --git a/Lib9c/Model/Quest/ItemGradeQuest.cs b/Lib9c/Model/Quest/ItemGradeQuest.cs index e123f13b2..5ad5d7c31 100644 --- a/Lib9c/Model/Quest/ItemGradeQuest.cs +++ b/Lib9c/Model/Quest/ItemGradeQuest.cs @@ -14,7 +14,7 @@ public class ItemGradeQuest : Quest { public readonly int Grade; public readonly List ItemIds = new List(); - public ItemGradeQuest(ItemGradeQuestSheet.Row data, QuestReward reward) + public ItemGradeQuest(ItemGradeQuestSheet.Row data, QuestReward reward) : base(data, reward) { Grade = data.Grade; @@ -62,13 +62,9 @@ public void Update(ItemUsable itemUsable) protected override string TypeId => "itemGradeQuest"; public override IValue Serialize() => -#pragma warning disable LAA1002 - new Dictionary(new Dictionary - { - [(Text)"grade"] = Grade.Serialize(), - [(Text)"itemIds"] = new List(ItemIds.OrderBy(i => i).Select(i => i.Serialize())), - }.Union((Dictionary)base.Serialize())); -#pragma warning restore LAA1002 + ((Dictionary)base.Serialize()) + .Add("grade", Grade.Serialize()) + .Add("itemIds", new List(ItemIds.OrderBy(i => i).Select(i => i.Serialize()))); } } diff --git a/Lib9c/Model/Quest/ItemTypeCollectQuest.cs b/Lib9c/Model/Quest/ItemTypeCollectQuest.cs index 64a8ed7d7..ca8a46acb 100644 --- a/Lib9c/Model/Quest/ItemTypeCollectQuest.cs +++ b/Lib9c/Model/Quest/ItemTypeCollectQuest.cs @@ -15,7 +15,7 @@ public class ItemTypeCollectQuest : Quest public readonly ItemType ItemType; public readonly List ItemIds = new List(); - public ItemTypeCollectQuest(ItemTypeCollectQuestSheet.Row data, QuestReward reward) + public ItemTypeCollectQuest(ItemTypeCollectQuestSheet.Row data, QuestReward reward) : base(data, reward) { ItemType = data.ItemType; @@ -64,13 +64,9 @@ public override string GetProgressText() => protected override string TypeId => "itemTypeCollectQuest"; public override IValue Serialize() => -#pragma warning disable LAA1002 - new Dictionary(new Dictionary - { - [(Text)"itemType"] = ItemType.Serialize(), - [(Text)"itemIds"] = new List(ItemIds.OrderBy(i => i).Select(i => i.Serialize())), - }.Union((Dictionary)base.Serialize())); -#pragma warning restore LAA1002 + ((Dictionary) base.Serialize()) + .Add("itemType", ItemType.Serialize()) + .Add("itemIds", new List(ItemIds.OrderBy(i => i).Select(i => i.Serialize()))); } } diff --git a/Lib9c/Model/Quest/MonsterQuest.cs b/Lib9c/Model/Quest/MonsterQuest.cs index b053d2c2e..60b781846 100644 --- a/Lib9c/Model/Quest/MonsterQuest.cs +++ b/Lib9c/Model/Quest/MonsterQuest.cs @@ -12,7 +12,7 @@ public class MonsterQuest : Quest { public readonly int MonsterId; - public MonsterQuest(MonsterQuestSheet.Row data, QuestReward reward) + public MonsterQuest(MonsterQuestSheet.Row data, QuestReward reward) : base(data, reward) { MonsterId = data.MonsterId; @@ -54,11 +54,7 @@ public void Update(CollectionMap monsterMap) } public override IValue Serialize() => -#pragma warning disable LAA1002 - new Dictionary(new Dictionary - { - [(Text)"monsterId"] = (Integer)MonsterId, - }.Union((Dictionary)base.Serialize())); -#pragma warning restore LAA1002 + ((Dictionary) base.Serialize()) + .Add("monsterId", MonsterId); } } diff --git a/Lib9c/Model/Quest/Quest.cs b/Lib9c/Model/Quest/Quest.cs index 55c378ca0..2d30d9897 100644 --- a/Lib9c/Model/Quest/Quest.cs +++ b/Lib9c/Model/Quest/Quest.cs @@ -82,16 +82,14 @@ protected Quest(Dictionary serialized) public abstract string GetProgressText(); public virtual IValue Serialize() => - new Dictionary(new Dictionary - { - [(Text) "typeId"] = (Text) TypeId, - [(Text) "complete"] = new Bencodex.Types.Boolean(Complete), - [(Text) "goal"] = (Integer) Goal, - [(Text) "current"] = (Integer) _current, - [(Text) "id"] = (Integer) Id, - [(Text) "reward"] = Reward.Serialize(), - [(Text) "isPaidInAction"] = new Bencodex.Types.Boolean(IsPaidInAction), - }); + Dictionary.Empty + .Add("typeId", (Text) TypeId) + .Add("complete", new Bencodex.Types.Boolean(Complete)) + .Add("goal", (Integer) Goal) + .Add("current", (Integer) _current) + .Add("id", (Integer) Id) + .Add("reward", Reward.Serialize()) + .Add("isPaidInAction", new Bencodex.Types.Boolean(IsPaidInAction)); public static Quest Deserialize(Dictionary serialized) { diff --git a/Lib9c/Model/Quest/TradeQuest.cs b/Lib9c/Model/Quest/TradeQuest.cs index acb5bc632..206697b9c 100644 --- a/Lib9c/Model/Quest/TradeQuest.cs +++ b/Lib9c/Model/Quest/TradeQuest.cs @@ -14,7 +14,7 @@ public class TradeQuest : Quest public override QuestType QuestType => QuestType.Exchange; public readonly TradeType Type; - public TradeQuest(TradeQuestSheet.Row data, QuestReward reward) + public TradeQuest(TradeQuestSheet.Row data, QuestReward reward) : base(data, reward) { Type = data.Type; @@ -45,11 +45,7 @@ public override string GetProgressText() => protected override string TypeId => "tradeQuest"; public override IValue Serialize() => -#pragma warning disable LAA1002 - new Dictionary(new Dictionary - { - [(Text)"type"] = (Integer)(int)Type, - }.Union((Dictionary)base.Serialize())); -#pragma warning restore LAA1002 + ((Dictionary) base.Serialize()) + .Add("type", (int) Type); } } diff --git a/Lib9c/Model/Skill/AreaAttack.cs b/Lib9c/Model/Skill/AreaAttack.cs index c49774e49..d115d7fce 100644 --- a/Lib9c/Model/Skill/AreaAttack.cs +++ b/Lib9c/Model/Skill/AreaAttack.cs @@ -17,13 +17,12 @@ public AreaAttack( { } - public override Model.BattleStatus.Skill Use( - CharacterBase caster, - int simulatorWaveTurn, - IEnumerable buffs) + public override BattleStatus.Skill Use(CharacterBase caster, + int simulatorWaveTurn, + IEnumerable buffs, bool copyCharacter) { - var clone = (CharacterBase) caster.Clone(); - var damage = ProcessDamage(caster, simulatorWaveTurn); + var clone = copyCharacter ? (CharacterBase) caster.Clone() : null; + var damage = ProcessDamage(caster, simulatorWaveTurn, copyCharacter: copyCharacter); var buff = ProcessBuff(caster, simulatorWaveTurn, buffs); return new Model.BattleStatus.AreaAttack(SkillRow.Id, clone, damage, buff); diff --git a/Lib9c/Model/Skill/AttackSkill.cs b/Lib9c/Model/Skill/AttackSkill.cs index b5c392ab2..6a2370d6a 100644 --- a/Lib9c/Model/Skill/AttackSkill.cs +++ b/Lib9c/Model/Skill/AttackSkill.cs @@ -27,9 +27,10 @@ protected AttackSkill( /// /// /// + /// /// protected IEnumerable ProcessDamage(CharacterBase caster, int simulatorWaveTurn, - bool isNormalAttack = false) + bool isNormalAttack = false, bool copyCharacter = true) { var infos = new List(); var targets = SkillRow.SkillTargetType.GetTarget(caster).ToList(); @@ -86,9 +87,10 @@ protected AttackSkill( target.CurrentHP -= damage; } - infos.Add(new BattleStatus.Skill.SkillInfo((CharacterBase) target.Clone(), damage, isCritical, + var clone = copyCharacter ? (CharacterBase) target.Clone() : null; + infos.Add(new BattleStatus.Skill.SkillInfo(target.Id, target.IsDead, target.Thorn, damage, isCritical, SkillRow.SkillCategory, simulatorWaveTurn, SkillRow.ElementalType, - SkillRow.SkillTargetType)); + SkillRow.SkillTargetType, target: clone)); } } diff --git a/Lib9c/Model/Skill/BlowAttack.cs b/Lib9c/Model/Skill/BlowAttack.cs index 6d0eb29ba..854380f93 100644 --- a/Lib9c/Model/Skill/BlowAttack.cs +++ b/Lib9c/Model/Skill/BlowAttack.cs @@ -17,13 +17,12 @@ public BlowAttack( { } - public override BattleStatus.Skill Use( - CharacterBase caster, + public override BattleStatus.Skill Use(CharacterBase caster, int simulatorWaveTurn, - IEnumerable buffs) + IEnumerable buffs, bool copyCharacter) { - var clone = (CharacterBase) caster.Clone(); - var damage = ProcessDamage(caster, simulatorWaveTurn); + var clone = copyCharacter ? (CharacterBase) caster.Clone() : null; + var damage = ProcessDamage(caster, simulatorWaveTurn, copyCharacter: copyCharacter); var buff = ProcessBuff(caster, simulatorWaveTurn, buffs); return new Model.BattleStatus.BlowAttack(SkillRow.Id, clone, damage, buff); diff --git a/Lib9c/Model/Skill/BuffRemovalAttack.cs b/Lib9c/Model/Skill/BuffRemovalAttack.cs index be103e777..f770ad95f 100644 --- a/Lib9c/Model/Skill/BuffRemovalAttack.cs +++ b/Lib9c/Model/Skill/BuffRemovalAttack.cs @@ -17,13 +17,12 @@ public BuffRemovalAttack( { } - public override BattleStatus.Skill Use( - CharacterBase caster, + public override BattleStatus.Skill Use(CharacterBase caster, int simulatorWaveTurn, - IEnumerable buffs) + IEnumerable buffs, bool copyCharacter) { - var clone = (CharacterBase) caster.Clone(); - var damage = ProcessDamage(caster, simulatorWaveTurn); + var clone = copyCharacter ? (CharacterBase) caster.Clone() : null; + var damage = ProcessDamage(caster, simulatorWaveTurn, copyCharacter: copyCharacter); var buff = ProcessBuff(caster, simulatorWaveTurn, buffs); var targets = SkillRow.SkillTargetType.GetTarget(caster); foreach (var target in targets) diff --git a/Lib9c/Model/Skill/BuffSkill.cs b/Lib9c/Model/Skill/BuffSkill.cs index a6d61f8c0..674ad3239 100644 --- a/Lib9c/Model/Skill/BuffSkill.cs +++ b/Lib9c/Model/Skill/BuffSkill.cs @@ -18,9 +18,9 @@ public BuffSkill( } public override BattleStatus.Skill Use(CharacterBase caster, int simulatorWaveTurn, - IEnumerable buffs) + IEnumerable buffs, bool copyCharacter) { - var clone = (CharacterBase) caster.Clone(); + var clone = copyCharacter ? (CharacterBase) caster.Clone() : null; var buff = ProcessBuff(caster, simulatorWaveTurn, buffs); return new BattleStatus.Buff(SkillRow.Id, clone, buff); diff --git a/Lib9c/Model/Skill/DoubleAttack.cs b/Lib9c/Model/Skill/DoubleAttack.cs index 35f003812..cab0ff643 100644 --- a/Lib9c/Model/Skill/DoubleAttack.cs +++ b/Lib9c/Model/Skill/DoubleAttack.cs @@ -17,13 +17,12 @@ public DoubleAttack( { } - public override BattleStatus.Skill Use( - CharacterBase caster, + public override BattleStatus.Skill Use(CharacterBase caster, int simulatorWaveTurn, - IEnumerable buffs) + IEnumerable buffs, bool copyCharacter) { - var clone = (CharacterBase) caster.Clone(); - var damage = ProcessDamage(caster, simulatorWaveTurn); + var clone = copyCharacter ? (CharacterBase) caster.Clone() : null; + var damage = ProcessDamage(caster, simulatorWaveTurn, copyCharacter: copyCharacter); var buff = ProcessBuff(caster, simulatorWaveTurn, buffs); return new Model.BattleStatus.DoubleAttack(SkillRow.Id, clone, damage, buff); diff --git a/Lib9c/Model/Skill/HealSkill.cs b/Lib9c/Model/Skill/HealSkill.cs index f7bbd5cf4..17c87aabe 100644 --- a/Lib9c/Model/Skill/HealSkill.cs +++ b/Lib9c/Model/Skill/HealSkill.cs @@ -17,15 +17,14 @@ public HealSkill( { } - public override BattleStatus.Skill Use( - CharacterBase caster, + public override BattleStatus.Skill Use(CharacterBase caster, int simulatorWaveTurn, - IEnumerable buffs) + IEnumerable buffs, bool copyCharacter) { - var clone = (CharacterBase) caster.Clone(); + var clone = copyCharacter ? (CharacterBase) caster.Clone() : null; var heal = ProcessHeal(caster, simulatorWaveTurn); var buff = ProcessBuff(caster, simulatorWaveTurn, buffs); - + return new BattleStatus.HealSkill(SkillRow.Id, clone, heal, buff); } @@ -42,8 +41,8 @@ public override BattleStatus.Skill Use( foreach (var target in SkillRow.SkillTargetType.GetTarget(caster)) { target.Heal(healPoint); - infos.Add(new BattleStatus.Skill.SkillInfo((CharacterBase)target.Clone(), healPoint, caster.IsCritical(false), - SkillRow.SkillCategory, simulatorWaveTurn)); + infos.Add(new BattleStatus.Skill.SkillInfo(target.Id, target.IsDead, target.Thorn, healPoint, caster.IsCritical(false), + SkillRow.SkillCategory, simulatorWaveTurn, target: target)); } return infos; diff --git a/Lib9c/Model/Skill/NormalAttack.cs b/Lib9c/Model/Skill/NormalAttack.cs index 96facc11c..e50205ba7 100644 --- a/Lib9c/Model/Skill/NormalAttack.cs +++ b/Lib9c/Model/Skill/NormalAttack.cs @@ -17,13 +17,12 @@ public NormalAttack( { } - public override Model.BattleStatus.Skill Use( - CharacterBase caster, + public override BattleStatus.Skill Use(CharacterBase caster, int simulatorWaveTurn, - IEnumerable buffs) + IEnumerable buffs, bool copyCharacter) { - var clone = (CharacterBase) caster.Clone(); - var damage = ProcessDamage(caster, simulatorWaveTurn, true); + var clone = copyCharacter ? (CharacterBase) caster.Clone() : null; + var damage = ProcessDamage(caster, simulatorWaveTurn, true, copyCharacter); var buff = ProcessBuff(caster, simulatorWaveTurn, buffs); return new Model.BattleStatus.NormalAttack(SkillRow.Id, clone, damage, buff); diff --git a/Lib9c/Model/Skill/Skill.cs b/Lib9c/Model/Skill/Skill.cs index 6985aa7e5..f75297fd7 100644 --- a/Lib9c/Model/Skill/Skill.cs +++ b/Lib9c/Model/Skill/Skill.cs @@ -36,11 +36,9 @@ protected Skill( ReferencedStatType = referencedStatType; } - public abstract BattleStatus.Skill Use( - CharacterBase caster, + public abstract BattleStatus.Skill Use(CharacterBase caster, int simulatorWaveTurn, - IEnumerable buffs - ); + IEnumerable buffs, bool copyCharacter); protected bool Equals(Skill other) { @@ -83,9 +81,9 @@ public override int GetHashCode() foreach (var target in targets.Where(target => target.GetChance(buff.BuffInfo.Chance))) { target.AddBuff(buff); - infos.Add(new Model.BattleStatus.Skill.SkillInfo((CharacterBase) target.Clone(), 0, false, + infos.Add(new Model.BattleStatus.Skill.SkillInfo(target.Id, target.IsDead, target.Thorn, 0, false, SkillRow.SkillCategory, simulatorWaveTurn, ElementalType.Normal, SkillRow.SkillTargetType, - buff)); + buff, target)); } } diff --git a/Lib9c/Model/State/RedeemCodeState.cs b/Lib9c/Model/State/RedeemCodeState.cs index d9b761faa..7554b3646 100644 --- a/Lib9c/Model/State/RedeemCodeState.cs +++ b/Lib9c/Model/State/RedeemCodeState.cs @@ -79,14 +79,12 @@ public RedeemCodeState(Dictionary rewardMap) } public override IValue Serialize() => + ((Dictionary) base.Serialize()) #pragma warning disable LAA1002 - new Dictionary(new Dictionary - { - [(Text) "map"] = new Dictionary(_map.Select(kv => new KeyValuePair( - kv.Key, - kv.Value.Serialize() - ))) - }.Union((Dictionary) base.Serialize())); + .Add("map", new Dictionary(_map.Select(kv => new KeyValuePair( + kv.Key, + kv.Value.Serialize() + )))); #pragma warning restore LAA1002 public int Redeem(string code, Address userAddress)