From ba2d7bef94d9676ec724426209de7af13c5253f0 Mon Sep 17 00:00:00 2001 From: sanguinerarogue Date: Mon, 21 Oct 2024 10:06:09 -0600 Subject: [PATCH 1/3] Adds crushing blows to enemy attack table and metrics --- proto/api.proto | 8 ++- sim/core/metrics_aggregator.go | 7 +++ sim/core/spell_outcome.go | 23 +++++++- sim/core/spell_result.go | 6 ++ sim/core/stats/stats.go | 1 + sim/core/target.go | 9 ++- .../detailed_results/dtps_metrics.tsx | 16 ++++++ ui/core/proto_utils/logs_parser.tsx | 4 +- ui/core/proto_utils/sim_result.ts | 56 +++++++++++++++++-- 9 files changed, 120 insertions(+), 10 deletions(-) diff --git a/proto/api.proto b/proto/api.proto index f8def4fab5..5bfa73f0d3 100644 --- a/proto/api.proto +++ b/proto/api.proto @@ -145,7 +145,7 @@ message ActionMetrics { bool is_passive = 5; } -// Metrics for a specific action, when cast at a particular target. +// Metrics for a specific action, when cast at a particular target. Next = 37 message TargetedActionMetrics { reserved 19, 20; reserved "crit_block_damage", "crit_blocks"; @@ -195,6 +195,9 @@ message TargetedActionMetrics { // # of times this action was a Blocked critical strike. int32 blocked_crits = 33; + // # of times this action was a Crush. + int32 crushes = 35; + // # of times this action was a Glance. int32 glances = 8; @@ -225,6 +228,9 @@ message TargetedActionMetrics { // Total glancing damage done to this target by this action. double glance_damage = 17; + // Total crushing damage done to this target by thi action. + double crush_damage = 36; + // Total block damage done to this target by this action. double block_damage = 18; diff --git a/sim/core/metrics_aggregator.go b/sim/core/metrics_aggregator.go index b78ec56cb1..211a7018d4 100644 --- a/sim/core/metrics_aggregator.go +++ b/sim/core/metrics_aggregator.go @@ -173,6 +173,7 @@ type SpellMetrics struct { TotalGlanceDamage float64 // Damage done by all glance casts of this spell. TotalBlockDamage float64 // Damage done by all block casts of this spell. TotalBlockedCritDamage float64 // Damage done by all blocked critical casts casts of this spell. + TotalCrushDamage float64 // Damage done by all crushed casts of this spell. TotalThreat float64 // Threat generated by all casts of this spell. TotalHealing float64 // Healing done by all casts of this spell. TotalCritHealing float64 // Healing done by all critical casts of this spell. @@ -196,6 +197,7 @@ type TargetedActionMetrics struct { Parries int32 Blocks int32 BlockedCrits int32 + Crushes int32 Damage float64 ResistedDamage float64 @@ -208,6 +210,7 @@ type TargetedActionMetrics struct { GlanceDamage float64 BlockDamage float64 BlockedCritDamage float64 + CrushDamage float64 Threat float64 Healing float64 CritHealing float64 @@ -234,6 +237,7 @@ func (tam *TargetedActionMetrics) ToProto(unitIndex int32) *proto.TargetedAction Parries: tam.Parries, Blocks: tam.Blocks, BlockedCrits: tam.BlockedCrits, + Crushes: tam.Crushes, Damage: tam.Damage, ResistedDamage: tam.ResistedDamage, CritDamage: tam.CritDamage, @@ -245,6 +249,7 @@ func (tam *TargetedActionMetrics) ToProto(unitIndex int32) *proto.TargetedAction GlanceDamage: tam.GlanceDamage, BlockDamage: tam.BlockDamage, BlockedCritDamage: tam.BlockedCritDamage, + CrushDamage: tam.CrushDamage, Threat: tam.Threat, Healing: tam.Healing, CritHealing: tam.CritHealing, @@ -391,6 +396,7 @@ func (unitMetrics *UnitMetrics) addSpellMetrics(spell *Spell, actionID ActionID, tam.Parries += spellTargetMetrics.Parries tam.Blocks += spellTargetMetrics.Blocks tam.BlockedCrits += spellTargetMetrics.BlockedCrits + tam.Crushes += spellTargetMetrics.Crushes tam.Glances += spellTargetMetrics.Glances tam.Damage += spellTargetMetrics.TotalDamage tam.ResistedDamage += spellTargetMetrics.TotalResistedDamage @@ -403,6 +409,7 @@ func (unitMetrics *UnitMetrics) addSpellMetrics(spell *Spell, actionID ActionID, tam.GlanceDamage += spellTargetMetrics.TotalGlanceDamage tam.BlockDamage += spellTargetMetrics.TotalBlockDamage tam.BlockedCritDamage += spellTargetMetrics.TotalBlockedCritDamage + tam.CrushDamage += spellTargetMetrics.TotalCrushDamage tam.Threat += spellTargetMetrics.TotalThreat tam.Healing += spellTargetMetrics.TotalHealing tam.CritHealing += spellTargetMetrics.TotalCritHealing diff --git a/sim/core/spell_outcome.go b/sim/core/spell_outcome.go index 0c358c234a..b1f39bf79b 100644 --- a/sim/core/spell_outcome.go +++ b/sim/core/spell_outcome.go @@ -641,7 +641,9 @@ func (spell *Spell) outcomeEnemyMeleeWhite(sim *Simulation, result *SpellResult, } if didHit && !result.applyEnemyAttackTableCrit(spell, attackTable, roll, &chance, countHits) { - result.applyAttackTableHit(spell, countHits) + if didHit && !result.applyEnemyAttackTableCrush(spell, attackTable, roll, &chance, countHits){ + result.applyAttackTableHit(spell, countHits) + } } } @@ -916,6 +918,25 @@ func (result *SpellResult) applyEnemyAttackTableCrit(spell *Spell, at *AttackTab return false } +func (result *SpellResult) applyEnemyAttackTableCrush(spell *Spell, at *AttackTable, roll float64, chance *float64, countHits bool) bool { + if !at.Attacker.PseudoStats.CanCrush { + return false + } + + crushChance := at.BaseCrushChance + *chance += max(0, crushChance) + + if roll < *chance { + result.Outcome = OutcomeCrush + if countHits { + spell.SpellMetrics[result.Target.UnitIndex].Crushes++ + } + result.Damage *= 1.5 + return true + } + return false +} + func (spell *Spell) OutcomeExpectedTick(_ *Simulation, _ *SpellResult, _ *AttackTable) { // result.Damage *= 1 } diff --git a/sim/core/spell_result.go b/sim/core/spell_result.go index 902a7bc249..e19abb637e 100644 --- a/sim/core/spell_result.go +++ b/sim/core/spell_result.go @@ -51,6 +51,10 @@ func (result *SpellResult) DidGlance() bool { return result.Outcome.Matches(OutcomeGlance) } +func (result *SpellResult) DidCrush() bool { + return result.Outcome.Matches(OutcomeCrush) +} + func (result *SpellResult) DidBlock() bool { return result.Outcome.Matches(OutcomeBlock) } @@ -418,6 +422,8 @@ func (spell *Spell) dealDamageInternal(sim *Simulation, isPeriodic bool, result spell.SpellMetrics[result.Target.UnitIndex].TotalGlanceDamage += result.Damage } else if result.DidBlock() { spell.SpellMetrics[result.Target.UnitIndex].TotalBlockDamage += result.Damage + } else if result.DidCrush() { + spell.SpellMetrics[result.Target.UnitIndex].TotalCrushDamage += result.Damage } spell.SpellMetrics[result.Target.UnitIndex].TotalThreat += result.Threat } diff --git a/sim/core/stats/stats.go b/sim/core/stats/stats.go index 4f4dbf8355..6116f28c2b 100644 --- a/sim/core/stats/stats.go +++ b/sim/core/stats/stats.go @@ -471,6 +471,7 @@ type PseudoStats struct { CanBlock bool CanParry bool + CanCrush bool Stunned bool // prevents blocks, dodges, and parries ParryHaste bool diff --git a/sim/core/target.go b/sim/core/target.go index b8669c0856..de73fc2823 100644 --- a/sim/core/target.go +++ b/sim/core/target.go @@ -3,7 +3,6 @@ package core import ( "strconv" "time" - "github.com/wowsims/sod/sim/core/proto" "github.com/wowsims/sod/sim/core/stats" ) @@ -143,6 +142,7 @@ func NewTarget(options *proto.Target, targetIndex int32) *Target { target.PseudoStats.CanBlock = true target.PseudoStats.CanParry = true + target.PseudoStats.CanCrush = true target.PseudoStats.ParryHaste = options.ParryHaste target.PseudoStats.InFrontOfTarget = true target.PseudoStats.DamageSpread = options.DamageSpread @@ -264,6 +264,7 @@ type AttackTable struct { BaseParryChance float64 BaseGlanceChance float64 BaseCritChance float64 + BaseCrushChance float64 GlanceMultiplierMin float64 GlanceMultiplierMax float64 @@ -316,8 +317,11 @@ func NewAttackTable(attacker *Unit, defender *Unit, weapon *Item) *AttackTable { table.BaseParryChance = 0.05 + (targetDefense-baseWeaponSkill)*0.001 // = 5 / 5.5 / 6 } + + table.BaseSpellMissChance = UnitLevelFloat64(defender.Level-attacker.Level, 0.04, 0.05, 0.06, 0.17) table.BaseBlockChance = 0.05 + table.BaseDodgeChance = 0.05 + (targetDefense-weaponSkill)*0.001 table.BaseGlanceChance = 0.1 + (targetDefense-baseWeaponSkill)*0.02 @@ -359,7 +363,8 @@ func NewAttackTable(attacker *Unit, defender *Unit, weapon *Item) *AttackTable { } else { table.BaseBlockChance = 0 } - + + table.BaseCrushChance = UnitLevelFloat64(attacker.Level-defender.Level, 0.0, 0.0, 0.0, 0.15) table.BaseMissChance = 0.05 + levelDelta table.BaseDodgeChance = levelDelta // base dodge applied with class base stats table.BaseCritChance = 0.05 - levelDelta diff --git a/ui/core/components/detailed_results/dtps_metrics.tsx b/ui/core/components/detailed_results/dtps_metrics.tsx index deac4ddc0e..e8543c24f1 100644 --- a/ui/core/components/detailed_results/dtps_metrics.tsx +++ b/ui/core/components/detailed_results/dtps_metrics.tsx @@ -48,6 +48,7 @@ export class DtpsMetricsTable extends MetricsTable { const critTickValues = metric.damageDone.critTick; const glanceValues = metric.damageDone.glance; const blockValues = metric.damageDone.block; + const crushValues = metric.damageDone.crush; cellElem.appendChild( { name: 'Blocked Hit', ...blockValues, }, + { + name: 'Crushing Blow', + ...crushValues, + }, ], }, ]} @@ -127,6 +132,11 @@ export class DtpsMetricsTable extends MetricsTable { value: metric.dodges, percentage: metric.dodgePercent, }, + { + name: 'Crushing Blow', + value: metric.crushes, + percentage: metric.crushPercent, + }, ], }, ]} @@ -168,6 +178,7 @@ export class DtpsMetricsTable extends MetricsTable { const relativeCritTickPercent = (metric.critTicks / metric.landedTicks) * 100; const relativeGlancePercent = (metric.glances / metric.landedHits) * 100; const relativeBlockPercent = (metric.blocks / metric.landedHits) * 100; + const relativeCrushPercent = (metric.crushes / metric.landedHits) * 100; cellElem.appendChild( { value: metric.glances, percentage: relativeGlancePercent, }, + { + name: 'Crushing Blow', + value: metric.crushes, + percentage: relativeCrushPercent, + }, { name: 'Blocked Hit', value: metric.blocks, diff --git a/ui/core/proto_utils/logs_parser.tsx b/ui/core/proto_utils/logs_parser.tsx index b8a80f806a..1b051db59d 100644 --- a/ui/core/proto_utils/logs_parser.tsx +++ b/ui/core/proto_utils/logs_parser.tsx @@ -305,12 +305,12 @@ export class DamageDealtLog extends SimLog { readonly miss: boolean; readonly hit: boolean; readonly crit: boolean; - readonly crush: boolean; readonly glance: boolean; readonly dodge: boolean; readonly parry: boolean; readonly block: boolean; readonly blockedCrit: boolean; + readonly crush: boolean; readonly tick: boolean; readonly partialResist1_4: boolean; readonly partialResist2_4: boolean; @@ -322,12 +322,12 @@ export class DamageDealtLog extends SimLog { type: string, miss: boolean, crit: boolean, - crush: boolean, glance: boolean, dodge: boolean, parry: boolean, block: boolean, blockedCrit: boolean, + crush: boolean, tick: boolean, partialResist1_4: boolean, partialResist2_4: boolean, diff --git a/ui/core/proto_utils/sim_result.ts b/ui/core/proto_utils/sim_result.ts index b4c617b7e5..5ad515fc82 100644 --- a/ui/core/proto_utils/sim_result.ts +++ b/ui/core/proto_utils/sim_result.ts @@ -811,7 +811,8 @@ export class ActionMetrics { this.avgCritTickDamage - this.avgGlanceDamage - this.avgBlockDamage - - this.avgBlockedCritDamage + this.avgBlockedCritDamage - + this.avgCrushDamage ); } @@ -882,6 +883,14 @@ export class ActionMetrics { return this.combinedMetrics.blockDamage; } + get crushDamage() { + return this.combinedMetrics.crushDamage; + } + + get avgCrushDamage() { + return this.combinedMetrics.avgCrushDamage; + } + get avgBlockDamage() { return this.combinedMetrics.avgBlockDamage; } @@ -1115,6 +1124,14 @@ export class ActionMetrics { return this.combinedMetrics.resistedCritTickPercent; } + get crushes() { + return this.combinedMetrics.crushes; + } + + get crushPercent() { + return this.combinedMetrics.crushPercent; + } + get blocks() { return this.combinedMetrics.blocks; } @@ -1176,7 +1193,8 @@ export class ActionMetrics { this.avgTickDamage - this.avgGlanceDamage - this.avgBlockDamage - - this.avgBlockedCritDamage + this.avgBlockedCritDamage - + this.avgCrushDamage ).toFixed(8), ); const normalResistedHitAvgDamage = Number( @@ -1247,6 +1265,11 @@ export class ActionMetrics { percentage: (this.avgBlockedCritDamage / this.avgDamage) * 100, average: this.avgBlockedCritDamage / this.blockedCrits, }, + crush: { + value: this.avgCrushDamage, + percentage: (this.avgCrushDamage / this.avgDamage) * 100, + average: this.avgCrushDamage / this.crushes, + }, }; } @@ -1333,11 +1356,18 @@ export class TargetedActionMetrics { this.duration = duration; this.data = data; - this.landedHitsRaw = this.data.hits + this.data.crits + this.data.blocks + this.data.blockedCrits + this.data.glances; + this.landedHitsRaw = this.data.hits + this.data.crits + this.data.blocks + this.data.blockedCrits + this.data.glances + this.data.crushes; this.landedTicksRaw = this.data.ticks + this.data.critTicks; this.hitAttempts = - this.data.misses + this.data.dodges + this.data.parries + this.data.blocks + this.data.blockedCrits + this.data.glances + this.data.crits; + this.data.misses + + this.data.dodges + + this.data.parries + + this.data.blocks + + this.data.blockedCrits + + this.data.glances + + this.data.crits + + this.data.crushes; if (this.data.hits != 0) { this.hitAttempts += this.data.hits; @@ -1418,6 +1448,14 @@ export class TargetedActionMetrics { return this.data.glanceDamage / this.iterations; } + get crushDamage() { + return this.data.crushDamage; + } + + get avgCrushDamage() { + return this.data.crushDamage / this.iterations; + } + get blockDamage() { return this.data.blockDamage; } @@ -1638,6 +1676,14 @@ export class TargetedActionMetrics { return this.data.blocks / this.iterations; } + get crushes() { + return this.data.crushes / this.iterations; + } + + get crushPercent() { + return (this.data.crushes / this.hitAttempts) * 100; + } + get blockPercent() { return (this.data.blocks / this.hitAttempts) * 100; } @@ -1701,6 +1747,7 @@ export class TargetedActionMetrics { dodges: sum(actions.map(a => a.data.dodges)), parries: sum(actions.map(a => a.data.parries)), blocks: sum(actions.map(a => a.data.blocks)), + crushes: sum(actions.map(a => a.data.crushes)), blockedCrits: sum(actions.map(a => a.data.blockedCrits)), glances: sum(actions.map(a => a.data.glances)), damage: sum(actions.map(a => a.data.damage)), @@ -1714,6 +1761,7 @@ export class TargetedActionMetrics { glanceDamage: sum(actions.map(a => a.data.glanceDamage)), blockDamage: sum(actions.map(a => a.data.blockDamage)), blockedCritDamage: sum(actions.map(a => a.data.blockedCritDamage)), + crushDamage: sum(actions.map(a => a.data.crushDamage)), threat: sum(actions.map(a => a.data.threat)), healing: sum(actions.map(a => a.data.healing)), critHealing: sum(actions.map(a => a.data.critHealing)), From e7ebbb8ea6dbbfd3bb096365976ebf043fea42be Mon Sep 17 00:00:00 2001 From: sanguinerarogue Date: Mon, 21 Oct 2024 10:31:52 -0600 Subject: [PATCH 2/3] Log Parser Fix --- sim/core/target.go | 3 --- ui/core/proto_utils/logs_parser.tsx | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/sim/core/target.go b/sim/core/target.go index de73fc2823..61dd639873 100644 --- a/sim/core/target.go +++ b/sim/core/target.go @@ -317,11 +317,8 @@ func NewAttackTable(attacker *Unit, defender *Unit, weapon *Item) *AttackTable { table.BaseParryChance = 0.05 + (targetDefense-baseWeaponSkill)*0.001 // = 5 / 5.5 / 6 } - - table.BaseSpellMissChance = UnitLevelFloat64(defender.Level-attacker.Level, 0.04, 0.05, 0.06, 0.17) table.BaseBlockChance = 0.05 - table.BaseDodgeChance = 0.05 + (targetDefense-weaponSkill)*0.001 table.BaseGlanceChance = 0.1 + (targetDefense-baseWeaponSkill)*0.02 diff --git a/ui/core/proto_utils/logs_parser.tsx b/ui/core/proto_utils/logs_parser.tsx index 1b051db59d..b8a80f806a 100644 --- a/ui/core/proto_utils/logs_parser.tsx +++ b/ui/core/proto_utils/logs_parser.tsx @@ -305,12 +305,12 @@ export class DamageDealtLog extends SimLog { readonly miss: boolean; readonly hit: boolean; readonly crit: boolean; + readonly crush: boolean; readonly glance: boolean; readonly dodge: boolean; readonly parry: boolean; readonly block: boolean; readonly blockedCrit: boolean; - readonly crush: boolean; readonly tick: boolean; readonly partialResist1_4: boolean; readonly partialResist2_4: boolean; @@ -322,12 +322,12 @@ export class DamageDealtLog extends SimLog { type: string, miss: boolean, crit: boolean, + crush: boolean, glance: boolean, dodge: boolean, parry: boolean, block: boolean, blockedCrit: boolean, - crush: boolean, tick: boolean, partialResist1_4: boolean, partialResist2_4: boolean, From 9ec206534a3ff6087c4dc27bc41ec74298dbbe73 Mon Sep 17 00:00:00 2001 From: sanguinerarogue <118020821+sanguinerarogue@users.noreply.github.com> Date: Mon, 21 Oct 2024 10:33:36 -0600 Subject: [PATCH 3/3] Update api.proto --- proto/api.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto/api.proto b/proto/api.proto index 5bfa73f0d3..bccfb2ea99 100644 --- a/proto/api.proto +++ b/proto/api.proto @@ -228,7 +228,7 @@ message TargetedActionMetrics { // Total glancing damage done to this target by this action. double glance_damage = 17; - // Total crushing damage done to this target by thi action. + // Total crushing damage done to this target by this action. double crush_damage = 36; // Total block damage done to this target by this action.