WOS Sim
← Back to Runs

Run Detail

3f564d56-5728-457b-80e1-b9c005fe33d2

Started11/06/2026, 04:01:25
Git SHA40ddd336
Statedirty
Avg Error %0.20%
BH Sig Count4
Total Cases178
Passing162
Run Report

Testcase Set Diff vs Previous Run

+ Added (1)

Code Changes Since Previous Run

Index: simulator/src/damage.ts
===================================================================
--- simulator/src/damage.ts prev run
+++ simulator/src/damage.ts this run
@@ -37,8 +37,9 @@
interface BucketCandidate {
effect: ActiveEffect;
bucket: AtomicBucket;
valuePct: number;
+ carriedInactive?: boolean;
}
interface MaxBucketCandidateGroup {
selected: BucketCandidate;
@@ -122,15 +123,19 @@
const rejectedEffects: DamageEquationTrace["rejectedEffects"] = detail === "full" ? [] : [];
const consumedEffectIds = new Set<string>();
const candidates: BucketCandidate[] = [];
const handledCandidateEffectIds = traceEnabled ? new Set<string>() : undefined;
+ const carriedAttackDurationEffectIds = job.carriedAttackDurationEffectIds ? new Set(job.carriedAttackDurationEffectIds) : undefined;
for (const candidate of bucketCandidatesForJob(options.effectIndex, job)) {
- if (!isEffectActive(candidate.effect, job.round)) continue;
+ const active = isEffectActive(candidate.effect, job.round);
+ const carriedInactive = !active && carriedAttackDurationEffectIds?.has(candidate.effect.id) === true;
+ if (!active && !carriedInactive) continue;
handledCandidateEffectIds?.add(candidate.effect.id);
candidates.push({
effect: candidate.effect,
bucket: candidate.bucket,
- valuePct: currentEffectValuePct(candidate.effect, job.round)
+ valuePct: currentEffectValuePct(candidate.effect, job.round),
+ carriedInactive
});
}
if (traceEnabled) {
@@ -266,9 +271,9 @@
if (selected.effect.stackingKey !== undefined) appliedEffect.stackingKey = selected.effect.stackingKey;
appliedEffects.push(appliedEffect);
}
for (const candidate of candidates) {
- if (candidate.effect.duration.type === "attack") consumedEffectIds.add(candidate.effect.id);
+ if (candidate.effect.duration.type === "attack" && !candidate.carriedInactive) consumedEffectIds.add(candidate.effect.id);
if (detail === "full" && candidate !== selected) {
rejectedEffects.push({ effectId: candidate.effect.source.effectId ?? candidate.effect.id, reason: "same_effect_max_suppressed" });
}
}
Index: simulator/src/simulator.test.ts
===================================================================
--- simulator/src/simulator.test.ts prev run
+++ simulator/src/simulator.test.ts this run
@@ -1322,8 +1322,89 @@
assert.deepEqual(skillBoosts, [100, 100, 0, 0]);
assert.deepEqual(result.extraSkillAttackJobsByEffect, { hitLiving: 4 });
});
+test("attack-duration normal damage effects do not carry to triggered extra skill damage by default", () => {
+ const result = simulateBattle(
+ {
+ maxRounds: 1,
+ trace: true,
+ attacker: {
+ troops: { marksman_t1: 100 },
+ heroes: { FollowUp: { skill_1: 1, skill_2: 1 } }
+ },
+ defender: {
+ troops: { infantry_t1: 100 },
+ heroes: {}
+ }
+ },
+ attackDurationCarryConfig()
+ );
+
+ const normalBoosts = result.attacks
+ .filter((attack) => attack.kind === "normal" && attack.attackerSide === "attacker")
+ .map((attack) => attack.trace?.atomicBuckets["active.hero.attack.up"].totalPct ?? 0);
+ const skillBoosts = result.attacks
+ .filter((attack) => attack.kind === "skill")
+ .map((attack) => attack.trace?.atomicBuckets["active.hero.attack.up"].totalPct ?? 0);
+ assert.deepEqual(normalBoosts, [100]);
+ assert.deepEqual(skillBoosts, [0]);
+});
+
+test("mechanics flag carries consumed attack-duration effects to matching triggered extra skill damage only", () => {
+ const result = simulateBattle(
+ {
+ maxRounds: 1,
+ trace: true,
+ mechanics: { carryAttackDurationEffectsToTriggeredExtraSkillDamage: true },
+ attacker: {
+ troops: { marksman_t1: 100 },
+ heroes: { FollowUp: { skill_1: 1, skill_2: 1 } }
+ },
+ defender: {
+ troops: { infantry_t1: 100, lancer_t1: 100, marksman_t1: 100 },
+ heroes: {}
+ }
+ },
+ attackDurationCarryConfig({ buffAppliesVs: "infantry", extraTargets: "enemy.living" })
+ );
+
+ const normalBoosts = result.attacks
+ .filter((attack) => attack.kind === "normal" && attack.attackerSide === "attacker")
+ .map((attack) => attack.trace?.atomicBuckets["active.hero.attack.up"].totalPct ?? 0);
+ const skillBoostsByTarget = result.attacks
+ .filter((attack) => attack.kind === "skill")
+ .map((attack) => `${attack.defenderUnit}:${attack.trace?.atomicBuckets["active.hero.attack.up"].totalPct ?? 0}`)
+ .sort();
+ assert.deepEqual(normalBoosts, [100]);
+ assert.deepEqual(skillBoostsByTarget, ["infantry:100", "lancer:0", "marksman:0"]);
+});
+
+test("mechanics flag carries consumed attack-duration effects with any target scope to all matching triggered extra skill damage", () => {
+ const result = simulateBattle(
+ {
+ maxRounds: 1,
+ trace: true,
+ mechanics: { carryAttackDurationEffectsToTriggeredExtraSkillDamage: true },
+ attacker: {
+ troops: { marksman_t1: 100 },
+ heroes: { FollowUp: { skill_1: 1, skill_2: 1 } }
+ },
+ defender: {
+ troops: { infantry_t1: 100, lancer_t1: 100, marksman_t1: 100 },
+ heroes: {}
+ }
+ },
+ attackDurationCarryConfig({ buffAppliesVs: "any", extraTargets: "enemy.living" })
+ );
+
+ const skillBoosts = result.attacks
+ .filter((attack) => attack.kind === "skill")
+ .map((attack) => attack.trace?.atomicBuckets["active.hero.attack.up"].totalPct ?? 0)
+ .sort((left, right) => left - right);
+ assert.deepEqual(skillBoosts, [100, 100, 100]);
+});
+
test("attack-triggered source and target selectors resolve to concrete active scopes", () => {
const result = simulateBattle(
{
maxRounds: 1,
@@ -1800,4 +1881,41 @@
troopSkills: { name: "troop skills", skills: {} },
diagnostics: { legacyFields: [], effectTypes: {}, unsupportedEffects: [], ambiguousTurnTriggerSelectors: [] }
};
}
+
+function attackDurationCarryConfig(
+ options: { buffAppliesVs?: "any" | "infantry"; extraTargets?: "use.target" | "enemy.living" } = {}
+): SimulatorConfig {
+ const buffAppliesVs = options.buffAppliesVs ?? "any";
+ const extraTargets = options.extraTargets ?? "use.target";
+ return minimalConfig({
+ FollowUp: {
+ name: "FollowUp",
+ skills: {
+ OneUseNormalBoost: {
+ trigger: { type: "battle_start" },
+ effects: {
+ boost: {
+ type: "active.hero.attack.up",
+ value: 100,
+ units: { applies_to: "marksman", applies_vs: buffAppliesVs },
+ duration: { type: "attack", value: 1 }
+ }
+ }
+ },
+ TriggeredExtraDamage: {
+ trigger: { type: "attack", probability: 100, source: "marksman" },
+ effects: {
+ extra: {
+ type: "extra_skill_attack",
+ value: 100,
+ units: { applies_to: "trigger.source", applies_vs: "any" },
+ trigger_damage_jobs: [{ source: "use.source", target: extraTargets }],
+ duration: { type: "attack", value: 1 }
+ }
+ }
+ }
+ }
+ }
+ });
+}
Index: simulator/src/simulator.ts
===================================================================
--- simulator/src/simulator.ts prev run
+++ simulator/src/simulator.ts this run
@@ -52,8 +52,9 @@
effectActivationCounts: Record<SideId, number>;
extraSkillAttackJobsByEffect: Record<string, number>;
attackControlCounts: { dodge: number; no_attack: number };
consumedEffectUseKeys: Set<string>;
+ carryAttackDurationEffectsToTriggeredExtraSkillDamage: boolean;
counters: {
attacks: Record<SideId, Record<UnitType, number>>;
received: Record<SideId, Record<UnitType, number>>;
};
@@ -111,9 +112,9 @@
function runBattle(input: BattleInput, config: SimulatorConfig, options: SimulationOptions): BattleRun {
const attacker = resolveFighter(input.attacker, "attacker", config, input.mechanics);
const defender = resolveFighter(input.defender, "defender", config, input.mechanics);
const fighters: Record<SideId, ResolvedFighter> = { attacker, defender };
- const runtime = createRuntime([attacker, defender], createSeededRng(input.seed ?? "simulator-default"));
+ const runtime = createRuntime([attacker, defender], createSeededRng(input.seed ?? "simulator-default"), input.mechanics);
const detail = options.detail ?? "full";
const traceEnabled = detail === "full" && input.trace;
const trace: BattleTrace | undefined = traceEnabled ? { resolved: buildResolved(attacker, defender), rounds: [] } : undefined;
const attacks: AttackOutcome[] = [];
@@ -288,9 +289,9 @@
const value = Array.isArray(probability) ? Number(probability[Math.max(0, Math.min(probability.length - 1, skill.level - 1))]) : Number(probability);
return Number.isFinite(value) && value > 0 && value < 100;
}
-function createRuntime(fighters: ResolvedFighter[], rng: Rng): Runtime {
+function createRuntime(fighters: ResolvedFighter[], rng: Rng, mechanics?: BattleInput["mechanics"]): Runtime {
const reports: Record<SideId, Map<string, SkillReportEntry>> = { attacker: new Map(), defender: new Map() };
const skills = buildRuntimeSkills(fighters);
for (const fighter of fighters) {
for (const skill of [...(fighter.heroSkills ?? []), ...fighter.troopSkills]) {
@@ -320,8 +321,9 @@
effectActivationCounts: { attacker: 0, defender: 0 },
extraSkillAttackJobsByEffect: {},
attackControlCounts: { dodge: 0, no_attack: 0 },
consumedEffectUseKeys: new Set(),
+ carryAttackDurationEffectsToTriggeredExtraSkillDamage: mechanics?.carryAttackDurationEffectsToTriggeredExtraSkillDamage === true,
counters: {
attacks: { attacker: emptyTroops(), defender: emptyTroops() },
received: { attacker: emptyTroops(), defender: emptyTroops() }
}
@@ -512,8 +514,11 @@
runtime: Runtime,
roundStartTroops: DamageJob["roundStartTroops"]
): DamageJob[] {
const jobs: DamageJob[] = [];
+ const carriedAttackDurationEffectIds = runtime.carryAttackDurationEffectsToTriggeredExtraSkillDamage
+ ? attackDurationBucketEffectIdsForJob(normalAttack, round, runtime.activeEffects)
+ : undefined;
const effectGroups = selectStackedExtraAttackEffectGroups(
runtime.effectIndex.extraAttacks.filter((effect) => isEffectActive(effect, round) && extraAttackEffectAppliesToNormalAttack(effect, normalAttack)),
round
);
@@ -547,8 +552,9 @@
defenderUnit: target.unit,
sourceEffectId,
sourceSkillReportKey,
sourceMultiplier: multiplier,
+ carriedAttackDurationEffectIds,
consumedEffectIds,
consumedEffectUseKey,
consumedEffectUseId: effect.id,
consumedEffectUseIds: consumedEffectIds
@@ -744,8 +750,18 @@
})
.map((effect) => effect.id);
}
+function attackDurationBucketEffectIdsForJob(job: DamageJob, round: number, effects: ActiveEffect[]): string[] {
+ return effects
+ .filter((effect) => {
+ if (effect.kind === "extra_attack" || effect.duration.type !== "attack" || !isEffectActive(effect, round)) return false;
+ const classification = classifyEffectForJob(effect, job);
+ return classification?.kind === "bucket";
+ })
+ .map((effect) => effect.id);
+}
+
function consumeEffects(
runtime: Runtime,
consumedEffectIds: string[],
consumedEffectUseKey?: string,
Index: simulator/src/types.ts
===================================================================
--- simulator/src/types.ts prev run
+++ simulator/src/types.ts this run
@@ -276,8 +276,9 @@
defenderUnit: UnitType;
sourceEffectId?: string;
sourceSkillReportKey?: string;
sourceMultiplier?: number;
+ carriedAttackDurationEffectIds?: string[];
consumedEffectIds?: string[];
consumedEffectUseKey?: string;
consumedEffectUseId?: string;
consumedEffectUseIds?: string[];
Index: testcases/emulator_verified/ambusher_no_marksmen.json
===================================================================
--- testcases/emulator_verified/ambusher_no_marksmen.json prev run
+++ testcases/emulator_verified/ambusher_no_marksmen.json this run
@@ -0,0 +1,61 @@
+[
+ {
+ "test_id": "ambusher_no_marksmen",
+ "description": "Field battle (direct attack, not a rally) , battle 2026-06-07 21:33:44. Pins Ambusher target-order fallback: attacker t10fc8 lancers have Ambusher (tier 7+) and the game report shows it triggering, but the defender fields ZERO marksmen. Game ground truth (defender per-type table): lancers 0 losses / 0 injured / 0 lightly injured, survivors 81,191 of 81,191; ALL defender damage fell on infantry (-3,085 injured, 7,198 lightly injured, 24,495 of 34,778 survivors). Proves ambush order falls back to infantry before lancers when marksmen are absent, i.e. order marksman -> infantry -> lancer, matching config troop_skills.json Ambusher. All hero skills level 5 (skills 1-3 only, widget skill 4 not relevant, no rally/garrison engagement). Survivor totals from Battle Overview Survivors row; per-skill triggers from Battle Details: defender Edith 1/1/1(+31), Renee 10/1/1 (2,373 skill kills), Wayne 10/-/8 (7,266 + 6,333 skill kills); attacker Edith 1/1/1(+25), Renee 10/1/1 (225 skill kills), Lynn 8/1/2 (146 skill kills).",
+ "attacker": {
+ "name": "[PWR]Longlive _Anata",
+ "heroes": {
+ "Edith": { "skill_1": 5, "skill_2": 5, "skill_3": 5 },
+ "Renee": { "skill_1": 5, "skill_2": 5, "skill_3": 5 },
+ "Lynn": { "skill_1": 5, "skill_2": 5, "skill_3": 5 }
+ },
+ "troops": {
+ "infantry_t11_fc8": 94385,
+ "lancer_t10_fc8": 37754,
+ "marksman_t10_fc8": 56632
+ },
+ "stats": {
+ "inf": { "attack": 1214.6, "defense": 1298.7, "lethality": 640.4, "health": 490.2 },
+ "lanc": { "attack": 1220.3, "defense": 1320.2, "lethality": 659.6, "health": 517.7 },
+ "mark": { "attack": 1134.9, "defense": 1177.3, "lethality": 739.1, "health": 565.4 }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminx",
+ "heroes": {
+ "Edith": { "skill_1": 5, "skill_2": 5, "skill_3": 5 },
+ "Renee": { "skill_1": 5, "skill_2": 5, "skill_3": 5 },
+ "Wayne": { "skill_1": 5, "skill_2": 5, "skill_3": 5 }
+ },
+ "troops": {
+ "infantry_t11_fc8": 34778,
+ "lancer_t11_fc8": 81191
+ },
+ "stats": {
+ "inf": { "attack": 1890.1, "defense": 1873.8, "lethality": 1595.0, "health": 1824.8 },
+ "lanc": { "attack": 1710.0, "defense": 1669.8, "lethality": 1183.3, "health": 1081.2 },
+ "mark": { "attack": 1810.0, "defense": 1766.9, "lethality": 1926.7, "health": 1667.3 }
+ },
+ "joiner_heroes": {}
+ },
+ "game_report_result": [
+ {
+ "timestamp": "2026-06-07 21:33",
+ "attacker": 0,
+ "defender": 105686
+ }
+ ],
+ "game_report_detail": {
+ "source": "read-only manual capture of open report, Piddlyminx instance, mail_id 2734682983265503",
+ "battle_overview": {
+ "attacker": { "troops": 188771, "losses": 0, "injured": 56633, "lightly_injured": 132138, "survivors": 0 },
+ "defender": { "troops": 115969, "losses": 0, "injured": 3085, "lightly_injured": 7198, "survivors": 105686 }
+ },
+ "defender_by_type": {
+ "infantry": { "kills": 7132, "losses": 0, "injured": 3085, "lightly_injured": 7198, "survivors": 24495 },
+ "lancer": { "kills": 49501, "losses": 0, "injured": 0, "lightly_injured": 0, "survivors": 81191 }
+ }
+ }
+ }
+]
Show Raw Dirty State Patch (vs Clean Baseline)
diff --git a/simulator/src/damage.ts b/simulator/src/damage.ts
--- a/simulator/src/damage.ts
+++ b/simulator/src/damage.ts
@@ -38,6 +38,7 @@
effect: ActiveEffect;
bucket: AtomicBucket;
valuePct: number;
+ carriedInactive?: boolean;
}
interface MaxBucketCandidateGroup {
@@ -123,13 +124,17 @@
const consumedEffectIds = new Set<string>();
const candidates: BucketCandidate[] = [];
const handledCandidateEffectIds = traceEnabled ? new Set<string>() : undefined;
+ const carriedAttackDurationEffectIds = job.carriedAttackDurationEffectIds ? new Set(job.carriedAttackDurationEffectIds) : undefined;
for (const candidate of bucketCandidatesForJob(options.effectIndex, job)) {
- if (!isEffectActive(candidate.effect, job.round)) continue;
+ const active = isEffectActive(candidate.effect, job.round);
+ const carriedInactive = !active && carriedAttackDurationEffectIds?.has(candidate.effect.id) === true;
+ if (!active && !carriedInactive) continue;
handledCandidateEffectIds?.add(candidate.effect.id);
candidates.push({
effect: candidate.effect,
bucket: candidate.bucket,
- valuePct: currentEffectValuePct(candidate.effect, job.round)
+ valuePct: currentEffectValuePct(candidate.effect, job.round),
+ carriedInactive
});
}
@@ -267,7 +272,7 @@
appliedEffects.push(appliedEffect);
}
for (const candidate of candidates) {
- if (candidate.effect.duration.type === "attack") consumedEffectIds.add(candidate.effect.id);
+ if (candidate.effect.duration.type === "attack" && !candidate.carriedInactive) consumedEffectIds.add(candidate.effect.id);
if (detail === "full" && candidate !== selected) {
rejectedEffects.push({ effectId: candidate.effect.source.effectId ?? candidate.effect.id, reason: "same_effect_max_suppressed" });
}
diff --git a/simulator/src/simulator.test.ts b/simulator/src/simulator.test.ts
--- a/simulator/src/simulator.test.ts
+++ b/simulator/src/simulator.test.ts
@@ -1323,6 +1323,87 @@
assert.deepEqual(result.extraSkillAttackJobsByEffect, { hitLiving: 4 });
});
+test("attack-duration normal damage effects do not carry to triggered extra skill damage by default", () => {
+ const result = simulateBattle(
+ {
+ maxRounds: 1,
+ trace: true,
+ attacker: {
+ troops: { marksman_t1: 100 },
+ heroes: { FollowUp: { skill_1: 1, skill_2: 1 } }
+ },
+ defender: {
+ troops: { infantry_t1: 100 },
+ heroes: {}
+ }
+ },
+ attackDurationCarryConfig()
+ );
+
+ const normalBoosts = result.attacks
+ .filter((attack) => attack.kind === "normal" && attack.attackerSide === "attacker")
+ .map((attack) => attack.trace?.atomicBuckets["active.hero.attack.up"].totalPct ?? 0);
+ const skillBoosts = result.attacks
+ .filter((attack) => attack.kind === "skill")
+ .map((attack) => attack.trace?.atomicBuckets["active.hero.attack.up"].totalPct ?? 0);
+ assert.deepEqual(normalBoosts, [100]);
+ assert.deepEqual(skillBoosts, [0]);
+});
+
+test("mechanics flag carries consumed attack-duration effects to matching triggered extra skill damage only", () => {
+ const result = simulateBattle(
+ {
+ maxRounds: 1,
+ trace: true,
+ mechanics: { carryAttackDurationEffectsToTriggeredExtraSkillDamage: true },
+ attacker: {
+ troops: { marksman_t1: 100 },
+ heroes: { FollowUp: { skill_1: 1, skill_2: 1 } }
+ },
+ defender: {
+ troops: { infantry_t1: 100, lancer_t1: 100, marksman_t1: 100 },
+ heroes: {}
+ }
+ },
+ attackDurationCarryConfig({ buffAppliesVs: "infantry", extraTargets: "enemy.living" })
+ );
+
+ const normalBoosts = result.attacks
+ .filter((attack) => attack.kind === "normal" && attack.attackerSide === "attacker")
+ .map((attack) => attack.trace?.atomicBuckets["active.hero.attack.up"].totalPct ?? 0);
+ const skillBoostsByTarget = result.attacks
+ .filter((attack) => attack.kind === "skill")
+ .map((attack) => `${attack.defenderUnit}:${attack.trace?.atomicBuckets["active.hero.attack.up"].totalPct ?? 0}`)
+ .sort();
+ assert.deepEqual(normalBoosts, [100]);
+ assert.deepEqual(skillBoostsByTarget, ["infantry:100", "lancer:0", "marksman:0"]);
+});
+
+test("mechanics flag carries consumed attack-duration effects with any target scope to all matching triggered extra skill damage", () => {
+ const result = simulateBattle(
+ {
+ maxRounds: 1,
+ trace: true,
+ mechanics: { carryAttackDurationEffectsToTriggeredExtraSkillDamage: true },
+ attacker: {
+ troops: { marksman_t1: 100 },
+ heroes: { FollowUp: { skill_1: 1, skill_2: 1 } }
+ },
+ defender: {
+ troops: { infantry_t1: 100, lancer_t1: 100, marksman_t1: 100 },
+ heroes: {}
+ }
+ },
+ attackDurationCarryConfig({ buffAppliesVs: "any", extraTargets: "enemy.living" })
+ );
+
+ const skillBoosts = result.attacks
+ .filter((attack) => attack.kind === "skill")
+ .map((attack) => attack.trace?.atomicBuckets["active.hero.attack.up"].totalPct ?? 0)
+ .sort((left, right) => left - right);
+ assert.deepEqual(skillBoosts, [100, 100, 100]);
+});
+
test("attack-triggered source and target selectors resolve to concrete active scopes", () => {
const result = simulateBattle(
{
@@ -1801,3 +1882,40 @@
diagnostics: { legacyFields: [], effectTypes: {}, unsupportedEffects: [], ambiguousTurnTriggerSelectors: [] }
};
}
+
+function attackDurationCarryConfig(
+ options: { buffAppliesVs?: "any" | "infantry"; extraTargets?: "use.target" | "enemy.living" } = {}
+): SimulatorConfig {
+ const buffAppliesVs = options.buffAppliesVs ?? "any";
+ const extraTargets = options.extraTargets ?? "use.target";
+ return minimalConfig({
+ FollowUp: {
+ name: "FollowUp",
+ skills: {
+ OneUseNormalBoost: {
+ trigger: { type: "battle_start" },
+ effects: {
+ boost: {
+ type: "active.hero.attack.up",
+ value: 100,
+ units: { applies_to: "marksman", applies_vs: buffAppliesVs },
+ duration: { type: "attack", value: 1 }
+ }
+ }
+ },
+ TriggeredExtraDamage: {
+ trigger: { type: "attack", probability: 100, source: "marksman" },
+ effects: {
+ extra: {
+ type: "extra_skill_attack",
+ value: 100,
+ units: { applies_to: "trigger.source", applies_vs: "any" },
+ trigger_damage_jobs: [{ source: "use.source", target: extraTargets }],
+ duration: { type: "attack", value: 1 }
+ }
+ }
+ }
+ }
+ }
+ });
+}
diff --git a/simulator/src/simulator.ts b/simulator/src/simulator.ts
--- a/simulator/src/simulator.ts
+++ b/simulator/src/simulator.ts
@@ -53,6 +53,7 @@
extraSkillAttackJobsByEffect: Record<string, number>;
attackControlCounts: { dodge: number; no_attack: number };
consumedEffectUseKeys: Set<string>;
+ carryAttackDurationEffectsToTriggeredExtraSkillDamage: boolean;
counters: {
attacks: Record<SideId, Record<UnitType, number>>;
received: Record<SideId, Record<UnitType, number>>;
@@ -112,7 +113,7 @@
const attacker = resolveFighter(input.attacker, "attacker", config, input.mechanics);
const defender = resolveFighter(input.defender, "defender", config, input.mechanics);
const fighters: Record<SideId, ResolvedFighter> = { attacker, defender };
- const runtime = createRuntime([attacker, defender], createSeededRng(input.seed ?? "simulator-default"));
+ const runtime = createRuntime([attacker, defender], createSeededRng(input.seed ?? "simulator-default"), input.mechanics);
const detail = options.detail ?? "full";
const traceEnabled = detail === "full" && input.trace;
const trace: BattleTrace | undefined = traceEnabled ? { resolved: buildResolved(attacker, defender), rounds: [] } : undefined;
@@ -289,7 +290,7 @@
return Number.isFinite(value) && value > 0 && value < 100;
}
-function createRuntime(fighters: ResolvedFighter[], rng: Rng): Runtime {
+function createRuntime(fighters: ResolvedFighter[], rng: Rng, mechanics?: BattleInput["mechanics"]): Runtime {
const reports: Record<SideId, Map<string, SkillReportEntry>> = { attacker: new Map(), defender: new Map() };
const skills = buildRuntimeSkills(fighters);
for (const fighter of fighters) {
@@ -321,6 +322,7 @@
extraSkillAttackJobsByEffect: {},
attackControlCounts: { dodge: 0, no_attack: 0 },
consumedEffectUseKeys: new Set(),
+ carryAttackDurationEffectsToTriggeredExtraSkillDamage: mechanics?.carryAttackDurationEffectsToTriggeredExtraSkillDamage === true,
counters: {
attacks: { attacker: emptyTroops(), defender: emptyTroops() },
received: { attacker: emptyTroops(), defender: emptyTroops() }
@@ -513,6 +515,9 @@
roundStartTroops: DamageJob["roundStartTroops"]
): DamageJob[] {
const jobs: DamageJob[] = [];
+ const carriedAttackDurationEffectIds = runtime.carryAttackDurationEffectsToTriggeredExtraSkillDamage
+ ? attackDurationBucketEffectIdsForJob(normalAttack, round, runtime.activeEffects)
+ : undefined;
const effectGroups = selectStackedExtraAttackEffectGroups(
runtime.effectIndex.extraAttacks.filter((effect) => isEffectActive(effect, round) && extraAttackEffectAppliesToNormalAttack(effect, normalAttack)),
round
@@ -548,6 +553,7 @@
sourceEffectId,
sourceSkillReportKey,
sourceMultiplier: multiplier,
+ carriedAttackDurationEffectIds,
consumedEffectIds,
consumedEffectUseKey,
consumedEffectUseId: effect.id,
@@ -745,6 +751,16 @@
.map((effect) => effect.id);
}
+function attackDurationBucketEffectIdsForJob(job: DamageJob, round: number, effects: ActiveEffect[]): string[] {
+ return effects
+ .filter((effect) => {
+ if (effect.kind === "extra_attack" || effect.duration.type !== "attack" || !isEffectActive(effect, round)) return false;
+ const classification = classifyEffectForJob(effect, job);
+ return classification?.kind === "bucket";
+ })
+ .map((effect) => effect.id);
+}
+
function consumeEffects(
runtime: Runtime,
consumedEffectIds: string[],
diff --git a/simulator/src/types.ts b/simulator/src/types.ts
--- a/simulator/src/types.ts
+++ b/simulator/src/types.ts
@@ -277,6 +277,7 @@
sourceEffectId?: string;
sourceSkillReportKey?: string;
sourceMultiplier?: number;
+ carriedAttackDurationEffectIds?: string[];
consumedEffectIds?: string[];
consumedEffectUseKey?: string;
consumedEffectUseId?: string;

Accuracy Results

178 / 178
FileCase#AdjS nG nS μG μBias%tqPW
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_90115302.05302.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_10111-1618.0-1618.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_11211-376.0-376.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_12311-6.0-6.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_134111462.01462.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_14511525.0525.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_156+0.015%11-254.0-254.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_167+0.009%111425.01425.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_178-0.003%11986.0986.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_189+0.024%11-177.0-177.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_191011-489.0-489.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_2011-0.01%11712.0712.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_2112+0.016%11-193.0-193.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_2213111309.01309.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_231411-277.0-277.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_2415-0.016%11585.0585.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_251611-45.0-45.00.00%P
testcases/1-testcases_no-heroes_t6_single-type_nc.jsondaut_viper_2617-0.002%11798.0798.00.00%P
testcases/2-testcases_no-heroes_t6_mixed_nc.jsondaut_viper_10+0.05%11735.0741.0-0.32%F
testcases/2-testcases_no-heroes_t6_mixed_nc.jsondaut_viper_21+0.044%11-61.0-61.00.00%P
testcases/2-testcases_no-heroes_t6_mixed_nc.jsondaut_viper_32+0.05%11771.0778.0-0.38%F
testcases/2-testcases_no-heroes_t6_mixed_nc.jsondaut_viper_43+0.05%11345.0353.0-0.45%F
testcases/2-testcases_no-heroes_t6_mixed_nc.jsondaut_viper_54+0.047%11264.0264.00.00%P
testcases/2-testcases_no-heroes_t6_mixed_nc.jsondaut_viper_65+0.05%11-90.0-89.0-0.07%P
testcases/3-testcases_mixed-heroes-not-verified.jsondaut_viper_10+0.043%11340.0342.0-0.13%F
testcases/3-testcases_mixed-heroes-not-verified.jsondaut_viper_211001-3729.0-3729.00.00%P
testcases/3-testcases_mixed-heroes-not-verified.jsondaut_viper_321001-2912.0-2823.0-0.41%-3.470.011P
testcases/3-testcases_mixed-heroes-not-verified.jsondaut_viper_431001-515.8-519.00.02%0.450.88P
testcases/3-testcases_mixed-heroes-not-verified.jsondaut_viper_5410019362.09282.00.64%1.860.34P
testcases/3-testcases_mixed-heroes-not-verified.jsondaut_viper_751001-2273.6-2295.00.16%2.260.24P
testcases/4-testcases_no-heroes_infantry_fc5.jsonNitro_Beast_30_1010013-45545.1-45251.0-0.22%-0.580.87P
testcases/emulator_verified/ahmose_solo_nc.jsonahmose_solo0-0.05%112636.02633.00.06%F
testcases/emulator_verified/alonso_attacker_600_all.jsonalonso_attacker_600_all01006-256.4-253.0-0.14%-0.750.84P
testcases/emulator_verified/alonso_attacker_900_inf_lancer.jsonalonso_attacker_900_inf_lancer01004-463.4-464.50.04%1.710.37P
testcases/emulator_verified/alonso_attacker_900_inf_lancer.jsonalonso_attacker_900_inf_lancer11002-459.3-460.00.03%0.650.87P
testcases/emulator_verified/alonso_minxxx_defender.jsonalonso_minxxx_defender010081453.41450.40.14%0.460.88P
testcases/emulator_verified/alonso_norah_combo.jsonalonso_norah_combo01002-392.9-393.00.00%0.030.97P
testcases/emulator_verified/alonso_norah_combo.jsonalonso_norah_combo11001-353.4-349.0-0.26%-1.650.37P
testcases/emulator_verified/alonso_norah_combo.jsonalonso_norah_combo21002-392.6-390.0-0.16%-1.260.60P
testcases/emulator_verified/alonso_solo.jsonalonso_solo0100101223.61104.82.55%1.040.69P
testcases/emulator_verified/alonso_solo_v2.jsonalonso_solo_v201006-493.4-513.71.56%0.890.75P
testcases/emulator_verified/alonso_solo_v2.jsonalonso_solo_v2110019-11.010.6-1.66%-1.120.66P
testcases/emulator_verified/alonso_vs_alonso.jsonalonso_vs_alonso010081551.01554.6-0.16%-0.650.87P
testcases/emulator_verified/alonso_vs_alonso_inf_lancer.jsonalonso_vs_alonso_inf_lancer01008-751.5-749.0-0.04%-0.480.88P
testcases/emulator_verified/alonso_wip_attacker.jsonalonso_wip_attacker010081768.21768.9-0.03%-0.400.91P
testcases/emulator_verified/ambusher_no_marksmen.jsonambusher_no_marksmen01001-103719.0-105686.00.65%1.000.70P
testcases/emulator_verified/attdef_jasser_patrick_minxxx_attacks_nc.jsonattdef_jasser_patrick_minxxx_attacks0+0.05%13-807.0-807.00.00%P
testcases/emulator_verified/attdef_jasser_patrick_nc.jsonattdef_jasser_patrick013-1138.0-1138.00.00%P
testcases/emulator_verified/attdef_jasser_patrick_swap_nc.jsonattdef_jasser_patrick_swap0131088.01088.00.00%P
testcases/emulator_verified/attdef_jasser_patrick_wip_attacks_nc.jsonattdef_jasser_patrick_wip_attacks0-0.05%11807.0807.00.00%P
testcases/emulator_verified/attdef_jasser_patrick_wip_attacks_nc.jsonattdef_jasser_patrick_wip_attacks1-0.05%12807.0807.00.00%P
testcases/emulator_verified/attdef_test_minxxx_attacks_nc.jsonattdef_test_minxxx_attacks_nc0-0.05%111133.01131.00.08%F
testcases/emulator_verified/attdef_test_wip_attacks_nc.jsonattdef_test_wip_attacks_nc012-1138.0-1138.00.00%P
testcases/emulator_verified/bahiti_norah_combo_nc.jsonbahiti_norah_combo_nc01001197.7269.0-2.97%-0.460.88P
testcases/emulator_verified/bahiti_solo.jsonbahiti_solo010022213.02206.00.15%0.150.95P
testcases/emulator_verified/bahiti_solo_nc.jsonbahiti_solo_nc010013677.03675.00.04%0.380.91P
testcases/emulator_verified/determinism_test_normal_nc.jsondeterminism_test_normal014852.0852.00.00%P
testcases/emulator_verified/determinism_test_swapped_nc.jsondeterminism_test_swapped011-852.0-852.00.00%P
testcases/emulator_verified/flint_reina_zinman_combo.jsonflint_reina_zinman_combo01001-442.5-439.0-0.08%-0.480.88P
testcases/emulator_verified/flint_solo_nc.jsonflint_solo0112862.02862.00.00%P
testcases/emulator_verified/flint_zinman_combo_nc.jsonflint_zinman_combo_nc0111005.01005.00.00%P
testcases/emulator_verified/greg_mia_combo.jsongreg_mia_combo0+0.049%10042391.02508.3-2.51%-1.840.34P
testcases/emulator_verified/greg_mia_combo.jsongreg_mia_combo1-0.04%10053475.13432.80.91%4.010.0026F
testcases/emulator_verified/greg_mia_defender_current.jsongreg_mia_defender_current0100183340.03351.6-0.25%-1.830.34P
testcases/emulator_verified/greg_mia_nohero_control_current.jsongreg_mia_nohero_control_current0133752.03752.00.00%P
testcases/emulator_verified/greg_mia_nohero_control_current.jsongreg_mia_nohero_control_current1+0.05%133652.03652.00.00%P
testcases/emulator_verified/greg_only_defender_current.jsongreg_only_defender_current010063682.73685.8-0.07%-0.960.70P
testcases/emulator_verified/greg_only_defender_current.jsongreg_only_defender_current110023582.23585.0-0.06%-0.560.87P
testcases/emulator_verified/greg_only_defender_current.jsongreg_only_defender_current2100203590.73594.6-0.08%-2.280.24P
testcases/emulator_verified/greg_solo.jsongreg_solo010012690.22697.0-0.15%-0.280.91P
testcases/emulator_verified/gwen_norah_combo_nc.jsongwen_norah_combo_nc010012698.42668.00.65%0.490.88P
testcases/emulator_verified/gwen_solo_nc.jsongwen_solo0-0.05%153017.03006.00.24%F
testcases/emulator_verified/hector_patrick_combo.jsonhector_patrick_combo01001-590.7-590.0-0.05%-0.970.70P
testcases/emulator_verified/hector_renee_wayne.jsondeterminism_test_normal0-0.05%10011607.81597.00.54%4.410.00086F
testcases/emulator_verified/hector_solo.jsonhector_solo01005-387.8-394.40.14%0.580.87P
testcases/emulator_verified/jasser_solo_nc.jsonjasser_solo0+0.05%112740.02740.00.00%P
testcases/emulator_verified/jessie_solo_nc.jsonjessie_solo0-0.05%1379.079.00.00%P
testcases/emulator_verified/ling_xue_solo_nc.jsonling_xue_solo0+0.05%112822.02822.00.00%P
testcases/emulator_verified/ling_xue_solo_nc.jsonling_xue_solo1+0.05%113214.03215.0-0.02%P
testcases/emulator_verified/logan_bahiti_combo_nc.jsonlogan_bahiti_combo_nc01001-507.1-517.00.55%0.810.82P
testcases/emulator_verified/logan_gwen_combo_nc.jsonlogan_gwen_combo_nc0-0.05%11-400.0-403.00.13%F
testcases/emulator_verified/logan_lynn_combo.jsonlogan_lynn_combo01001-551.0-553.00.09%1.240.60P
testcases/emulator_verified/logan_lynn_combo.jsonlogan_lynn_combo11002550.9550.50.02%0.310.91P
testcases/emulator_verified/logan_lynn_combo.jsonlogan_lynn_combo21002550.8551.0-0.01%-0.220.94P
testcases/emulator_verified/logan_reina_bahiti_combo_nc.jsonlogan_reina_bahiti_combo_nc01001-492.8-482.0-0.49%-0.710.84P
testcases/emulator_verified/logan_solo_minxxx_nc.jsonlogan_solo_minxxx011-1995.0-1995.00.00%P
testcases/emulator_verified/logan_solo_v2_nc.jsonlogan_solo_v2011-1134.0-1134.00.00%P
testcases/emulator_verified/lumak_bokan_solo_nc.jsonlumak_bokan_solo0+0.05%112797.02797.00.00%P
testcases/emulator_verified/lumak_bokan_solo_nc.jsonlumak_bokan_solo1+0.05%113127.03127.00.00%P
testcases/emulator_verified/lynn_solo.jsonlynn_solo01005-185.3-181.2-0.08%-0.480.88P
testcases/emulator_verified/mia_only_defender_current.jsonmia_only_defender_current0100103538.63543.9-0.11%-1.090.66P
testcases/emulator_verified/mia_only_defender_current.jsonmia_only_defender_current1100203437.93449.2-0.25%-2.850.074P
testcases/emulator_verified/mia_solo.jsonmia_solo01001-269.3-260.0-0.27%-0.970.70P
testcases/emulator_verified/mia_solo.jsonmia_solo110011297.81293.00.21%0.580.87P
testcases/emulator_verified/mia_solo.jsonmia_solo21001-184.5-180.0-0.29%-0.910.74P
testcases/emulator_verified/mia_solo.jsonmia_solo31001879.6879.00.04%0.140.95P
testcases/emulator_verified/mia_solo.jsonmia_solo41001969.7967.00.19%0.610.87P
testcases/emulator_verified/molly_lynn_combo.jsonmolly_lynn_combo01005-402.4-402.80.02%0.370.91P
testcases/emulator_verified/molly_solo.jsonmolly_solo01005244.0244.8-0.07%-1.670.37P
testcases/emulator_verified/natalia_solo.jsonnatalia_solo0+0.015%10012608.92664.0-1.18%-2.050.32F
testcases/emulator_verified/norah_greg_combo.jsonnorah_greg_combo01005-367.5-366.0-0.09%-1.220.60P
testcases/emulator_verified/norah_hector_zinman_combo.jsonnorah_hector_zinman_combo01005-269.2-270.40.08%1.650.37P
testcases/emulator_verified/norah_s2_inf_only_A.jsonnorah_s2_inf_only_A0+0.05%100492.798.5-0.35%-1.190.61P
testcases/emulator_verified/norah_s2_inf_only_B_nc.jsonnorah_s2_inf_only_B0+0.05%13507.0507.00.00%P
testcases/emulator_verified/norah_s2_splash_A.jsonnorah_s2_splash_A010011785.21780.00.23%0.350.91P
testcases/emulator_verified/norah_s2_splash_B_nc.jsonnorah_s2_splash_B0131900.01900.00.00%P
testcases/emulator_verified/norah_s2_splash_C_nc.jsonnorah_s2_splash_C0131153.01153.00.00%P
testcases/emulator_verified/norah_s2_splash_D.jsonnorah_s2_splash_D01003829.5827.30.12%0.720.84P
testcases/emulator_verified/norah_solo.jsonnorah_solo010072739.72753.1-0.29%-0.720.84P
testcases/emulator_verified/norah_zinman_combo_nc.jsonnorah_zinman_combo_nc01001679.3670.00.39%0.130.95P
testcases/emulator_verified/patrick_solo_nc.jsonpatrick_solo0+0.044%112582.02582.00.00%P
testcases/emulator_verified/philly_bahiti_combo.jsonphilly_bahiti_combo01001-558.0-556.0-0.11%-1.090.66P
testcases/emulator_verified/philly_bahiti_combo.jsonphilly_bahiti_combo11004-453.0-458.30.29%2.190.24P
testcases/emulator_verified/philly_solo.jsonphilly_solo010022260.92253.00.17%0.140.95P
testcases/emulator_verified/philly_solo.jsonphilly_solo110033620.53619.70.02%0.120.95P
testcases/emulator_verified/reina_attacker_wip.jsonreina_attacker_wip01001-1990.9-1992.00.05%2.270.24P
testcases/emulator_verified/reina_bahiti_combo_nc.jsonreina_bahiti_combo_nc01001-481.7-502.01.13%1.260.60P
testcases/emulator_verified/reina_logan_combo_simulator.jsonreina_logan_combo_simulator01001-549.5-553.00.14%1.280.60P
testcases/emulator_verified/reina_solo_wip.jsonreina_solo_wip010011991.01992.0-0.05%-2.240.24P
testcases/emulator_verified/renee_solo_nc.jsonrenee_solo_nc0+0.033%15-562.0-554.0-0.43%F
testcases/emulator_verified/seo_yoon_solo_nc.jsonseo_yoon_solo0-0.05%13-619.0-620.00.10%P
testcases/emulator_verified/sergey_norah_zinman_combo_nc.jsonsergey_norah_zinman_combo_nc010011325.71372.0-1.54%-0.740.84P
testcases/emulator_verified/sergey_reina_combo_nc.jsonsergey_reina_combo_nc01001-303.3-295.0-0.35%-0.290.91P
testcases/emulator_verified/sergey_solo_nc.jsonsergey_solo0-0.05%121348.01348.00.00%P
testcases/emulator_verified/simple_001_nc.jsonsimple_00101001-186.0-186.00.00%P
testcases/emulator_verified/troop_check_nc.jsontroop_check011-279.0-279.00.00%P
testcases/emulator_verified/type_triangle_A_nc.jsontype_triangle_A_nc0+0.05%11-895.0-895.00.00%P
testcases/emulator_verified/type_triangle_B_nc.jsontype_triangle_B_nc011-929.0-929.00.00%P
testcases/emulator_verified/wayne_mixed_solo.jsonwayne_mixed_solo01004234.4237.0-0.15%-0.070.96P
testcases/emulator_verified/wayne_mixed_solo.jsonwayne_mixed_solo11007307.6346.6-2.16%-1.940.34P
testcases/emulator_verified/wayne_reina_combo.jsonwayne_reina_combo010081550.01559.4-0.39%-1.800.35P
testcases/emulator_verified/wayne_reina_wuming_combo.jsonwayne_reina_wuming_combo010081697.81694.00.16%1.860.34P
testcases/emulator_verified/wayne_s1_solo.jsonwayne_s1_solo01005254.0251.80.36%1.300.60P
testcases/emulator_verified/wayne_s2_solo.jsonwayne_s2_solo01002363.7361.50.43%1.250.60P
testcases/emulator_verified/wos425_wip_nohero_control_nc.jsonwos425_wip_nohero_control_nc011774.0774.00.00%P
testcases/emulator_verified/wos425_wip_patrick_health_nc.jsonwos425_wip_patrick_health_nc011762.0762.00.00%P
testcases/emulator_verified/wos425_wip_philly_defense.jsonwos425_wip_philly_defense01005768.4768.6-0.02%-0.590.87P
testcases/emulator_verified/wos425_wip_sergey_damage_taken_nc.jsonwos425_wip_sergey_damage_taken_nc011749.0749.00.00%P
testcases/emulator_verified/wos425_wip_sergey_patrick_combo_nc.jsonwos425_wip_sergey_patrick_combo_nc011726.0726.00.00%P
testcases/emulator_verified/wos425_wip_sergey_philly_combo.jsonwos425_wip_sergey_philly_combo01005738.1738.2-0.01%-0.240.93P
testcases/emulator_verified/wos427_wip_sergey_philly_840.jsonwos427_wip_sergey_philly_8400100417.922.5-0.28%-0.310.91P
testcases/emulator_verified/wu_ming_solo_nc.jsonwu_ming_solo0111392.01392.00.00%P
testcases/emulator_verified/zinman_solo_nc.jsonzinman_solo0+0.05%113177.03177.00.00%P
testcases/heroes_unittests/Flint_tc.jsondaut_viper_10+0.05%15-150.0-149.8-0.01%P
testcases/heroes_unittests/Flint_tc.jsondaut_viper_21-0.03%165762.05732.70.41%F
testcases/heroes_unittests/Hector_tc.jsondaut_viper_101001-441.5-441.00.00%-0.100.95P
testcases/heroes_unittests/Jessie_tc_nc.jsondaut_viper_10+0.044%11-61.0-61.00.00%P
testcases/heroes_unittests/Jessie_tc_nc.jsondaut_viper_21+0.05%11-90.0-89.0-0.07%P
testcases/heroes_unittests/Jessie_tc_nc.jsondaut_viper_32-0.039%11481.0481.00.00%P
testcases/heroes_unittests/Jessie_tc_nc.jsondaut_viper_43-0.024%11217.0217.00.00%P
testcases/heroes_unittests/Jessie_tc_nc.jsondaut_viper_5411578.0578.00.00%P
testcases/heroes_unittests/Jessie_tc_nc.jsondaut_viper_65+0.027%11-96.0-96.00.00%P
testcases/heroes_unittests/Jessie_tc_nc.jsondaut_viper_76+0.038%11-74.0-74.00.00%P
testcases/heroes_unittests/Jessie_tc_nc.jsondaut_viper_8711-988.0-988.00.00%P
testcases/heroes_unittests/Mia_tc.jsondaut_viper_101001-173.4-181.00.17%1.650.37P
testcases/heroes_unittests/Mia_tc.jsondaut_viper_21+0.015%10071582.81625.1-0.97%-1.880.34P
testcases/heroes_unittests/Mia_tc.jsondaut_viper_3210061121.91124.2-0.10%-1.450.52P
testcases/heroes_unittests/Mia_tc.jsondaut_viper_431001667.7671.0-0.15%-0.330.91P
testcases/heroes_unittests/Mia_tc.jsondaut_viper_541004-124.2-121.8-0.11%-1.240.60P
testcases/heroes_unittests/Molly_tc.jsondaut_viper_10+0.044%11-61.0-61.00.00%P
testcases/heroes_unittests/Molly_tc.jsondaut_viper_21+0.05%11-90.0-89.0-0.07%P
testcases/heroes_unittests/Molly_tc.jsondaut_viper_32-0.039%11481.0481.00.00%P
testcases/heroes_unittests/Molly_tc.jsondaut_viper_431001-63.5-68.00.33%3.760.0048P
testcases/heroes_unittests/Molly_tc.jsondaut_viper_541001-190.3-190.0-0.01%-0.160.95P
testcases/heroes_unittests/Reina_tc.jsondaut_viper_10+0.05%11-66.0-62.0-0.18%F
testcases/heroes_unittests/Reina_tc.jsondaut_viper_2111-40.0-40.00.00%P
testcases/heroes_unittests/Reina_tc.jsondaut_viper_4210061413.41428.3-0.46%-0.280.91P
testcases/heroes_unittests/Reina_tc.jsondaut_viper_531001-201.9-202.00.01%0.060.96P
testcases/heroes_unittests/Seo-yoon_tc_nc.jsondaut_viper_10+0.044%11-61.0-61.00.00%P
testcases/heroes_unittests/Seo-yoon_tc_nc.jsondaut_viper_61+0.05%11-90.0-89.0-0.07%P
testcases/heroes_unittests/Seo-yoon_tc_nc.jsondaut_viper_32-0.039%11481.0481.00.00%P
testcases/heroes_unittests/Seo-yoon_tc_nc.jsondaut_viper_43+0.05%114339.04341.0-0.04%F
testcases/heroes_unittests/Seo-yoon_tc_nc.jsondaut_viper_54+0.05%113802.03805.0-0.06%F
testcases/heroes_unittests/Seo-yoon_tc_nc.jsondaut_viper_65-0.03%11915.0915.00.00%P
testcases/heroes_unittests/Seo-yoon_tc_nc.jsondaut_viper_76-0.034%112129.02128.00.02%P
testcases/heroes_unittests/Zinman_tc.jsondaut_viper_10-0.05%11-122.0-122.00.00%P
testcases/heroes_unittests/Zinman_tc.jsondaut_viper_21-0.032%114018.04018.00.00%P
testcases/heroes_unittests/Zinman_tc.jsondaut_viper_32-0.05%11-130.0-130.00.00%P