WOS Sim
← Back to Runs

Compare Runs

A (baseline)
97791858-a382b3f792825/09/2026, 10:55:22
B (current)
2f59ae69-63cfde24e4d29/09/2026, 20:27:43
Avg Error A0.13%
Avg Error B0.11%
Δ Avg Error-0.02%
Changed20
Improved19
Regressed1
Added128
Retired1

Testcase Delta

542 / 542
FileCase#AdjA%B%Δ%Status
testcases/gatot_verified/s3-5000-inf-vs-1000-inf-10-lancer.jsons3-5000-inf-vs-1000-inf-10-lancer0+0.013%7.04%0.00%-7.04%improved
testcases/emulator_verified/ambusher_logan_renee_vs_logan_patrick_nc.jsonambusher_logan_renee_vs_logan_patrick_nc0—-5.37%2.28%-3.09%improved
testcases/emulator_verified/ahmose_gordon_renee/ahmose_wayne_vs_jasser.jsonahmose_wayne_vs_jasser0—-2.34%0.00%-2.34%improved
testcases/emulator_verified/ahmose_gordon_renee/ahmose_wayne_vs_wuming.jsonahmose_wayne_vs_wuming0—-2.10%0.00%-2.10%improved
testcases/emulator_verified/ahmose_gordon_renee/ahmose_gordon_vs_wuming.jsonahmose_gordon_vs_wuming0—1.63%0.00%-1.63%improved
testcases/emulator_verified/ahmose_gordon_renee/ahmose_gordon_wayne_vs_jasser.jsonahmose_gordon_wayne_vs_jasser0—-0.38%0.00%-0.38%improved
testcases/emulator_verified/renee_s2_bucket_confirmations/gwen_next_hit_150_1050.jsongwen_next_hit_150_10500—-0.25%0.00%-0.25%improved
testcases/emulator_verified/renee_s2_families/renee_gwen.jsonrenee_gwen0—-0.09%0.00%-0.09%improved
testcases/emulator_verified/renee_s2_families/renee_gwen_no_marksmen.jsonrenee_gwen_no_marksmen0—-0.09%0.00%-0.09%improved
testcases/emulator_verified/attdef_test_minxxx_attacks_nc.jsonattdef_test_minxxx_attacks_nc0—0.08%0.00%-0.08%improved
testcases/emulator_verified/renee_s2_families/renee_gwen_25_marksmen.jsonrenee_gwen_25_marksmen0—-0.08%0.00%-0.08%improved
testcases/3-testcases_mixed-heroes-not-verified.jsonno_heroes_t6_mixed_2x_10-0.005%0.06%0.00%-0.06%improved
testcases/emulator_verified/gwen_c2_twenty/wu_ming_gordon_gwen_vs_gatot_jessie_hendrik.jsonwu_ming_gordon_gwen_vs_gatot_jessie_hendrik0—0.06%0.00%-0.06%improved
testcases/emulator_verified/renee_isolation_20260925/renee_gwen_300l_100m_vs_2700l.jsonrenee_gwen_300l_100m_vs_2700l0—-0.06%0.00%-0.06%improved
testcases/gatot_verified/s2-health-1.1-5100-vs-1000.jsons2-health-1.1-5100-vs-10000+0.001%-0.05%0.00%-0.05%improved
testcases/emulator_verified/interaction_map/gordon_wuming_pure_lancers.jsongordon_wuming_pure_lancers0—0.03%0.00%-0.03%improved
testcases/gatot_verified/s2-health-1.1-5050-vs-1000.jsons2-health-1.1-5050-vs-10000+0.001%-0.03%0.00%-0.03%improved
testcases/emulator_verified/wos444_gordon_s22_all_enemy_damage_dealt_hero_nc.jsonwos444_gordon_s22_all_enemy_damage_dealt_hero_nc0—-0.06%0.08%+0.02%regressed
testcases/gatot_verified/s2-health-1.1-4950-vs-1000.jsons2-health-1.1-4950-vs-10000+0.001%-0.02%0.00%-0.02%improved
testcases/gatot_verified/s2-health-1.1-5000-vs-1000.jsons2-health-1.1-5000-vs-10000+0.001%-0.02%0.00%-0.02%improved
testcases/1-testcases_no-heroes_t6_single-type_nc.jsonno_heroes_single_t6__101—0.00%0.00%0.00%unchanged
testcases/1-testcases_no-heroes_t6_single-type_nc.jsonno_heroes_single_t6__112—0.00%0.00%0.00%unchanged
testcases/1-testcases_no-heroes_t6_single-type_nc.jsonno_heroes_single_t6__123+0.005%0.00%0.00%0.00%unchanged
testcases/1-testcases_no-heroes_t6_single-type_nc.jsonno_heroes_single_t6__134+0.005%0.00%0.00%0.00%unchanged
testcases/1-testcases_no-heroes_t6_single-type_nc.jsonno_heroes_single_t6__145+0.004%0.00%0.00%0.00%unchanged
testcases/1-testcases_no-heroes_t6_single-type_nc.jsonno_heroes_single_t6__156+0.005%0.00%0.00%0.00%unchanged
testcases/1-testcases_no-heroes_t6_single-type_nc.jsonno_heroes_single_t6__167+0.005%0.00%0.00%0.00%unchanged
testcases/1-testcases_no-heroes_t6_single-type_nc.jsonno_heroes_single_t6__178+0.001%0.00%0.00%0.00%unchanged
testcases/1-testcases_no-heroes_t6_single-type_nc.jsonno_heroes_single_t6__189+0.005%0.00%0.00%0.00%unchanged
testcases/1-testcases_no-heroes_t6_single-type_nc.jsonno_heroes_single_t6__1910—0.00%0.00%0.00%unchanged
testcases/1-testcases_no-heroes_t6_single-type_nc.jsonno_heroes_single_t6__2011—0.00%0.00%0.00%unchanged
testcases/1-testcases_no-heroes_t6_single-type_nc.jsonno_heroes_single_t6__2112—0.00%0.00%0.00%unchanged
testcases/1-testcases_no-heroes_t6_single-type_nc.jsonno_heroes_single_t6__2213+0.004%0.00%0.00%0.00%unchanged
testcases/1-testcases_no-heroes_t6_single-type_nc.jsonno_heroes_single_t6__2314—0.00%0.00%0.00%unchanged
testcases/1-testcases_no-heroes_t6_single-type_nc.jsonno_heroes_single_t6__2415+0.005%0.00%0.00%0.00%unchanged
testcases/1-testcases_no-heroes_t6_single-type_nc.jsonno_heroes_single_t6__2516—0.00%0.00%0.00%unchanged
testcases/1-testcases_no-heroes_t6_single-type_nc.jsonno_heroes_single_t6__2617+0.001%0.00%0.00%0.00%unchanged
testcases/1-testcases_no-heroes_t6_single-type_nc.jsonno_heroes_single_t6__90+0.002%0.00%0.00%0.00%unchanged
testcases/2-testcases_no-heroes_t6_mixed_nc.jsonno_heroes_t6_mixed_10—0.00%0.00%0.00%unchanged
testcases/2-testcases_no-heroes_t6_mixed_nc.jsonno_heroes_t6_mixed_21—0.00%0.00%0.00%unchanged
testcases/2-testcases_no-heroes_t6_mixed_nc.jsonno_heroes_t6_mixed_32+0.005%0.00%0.00%0.00%unchanged
testcases/2-testcases_no-heroes_t6_mixed_nc.jsonno_heroes_t6_mixed_43—0.00%0.00%0.00%unchanged
testcases/2-testcases_no-heroes_t6_mixed_nc.jsonno_heroes_t6_mixed_54—0.00%0.00%0.00%unchanged
testcases/2-testcases_no-heroes_t6_mixed_nc.jsonno_heroes_t6_mixed_65—0.00%0.00%0.00%unchanged
testcases/3-testcases_mixed-heroes-not-verified.jsonno_heroes_t6_mixed_2x_21—0.00%0.00%0.00%unchanged
testcases/3-testcases_mixed-heroes-not-verified.jsonno_heroes_t6_mixed_2x_32+0.005%-0.40%-0.40%0.00%unchanged
testcases/3-testcases_mixed-heroes-not-verified.jsonno_heroes_t6_mixed_2x_43—0.02%0.02%0.00%unchanged
testcases/3-testcases_mixed-heroes-not-verified.jsonno_heroes_t6_mixed_2x_54—0.43%0.43%0.00%unchanged
testcases/3-testcases_mixed-heroes-not-verified.jsonno_heroes_t6_mixed_2x_65—0.17%0.17%0.00%unchanged
testcases/4-testcases_no-heroes_infantry_fc5.jsonNitro_Beast_30_10—0.18%0.18%0.00%unchanged
testcases/emulator_verified/ahmose_blade_categories/ahmose_vs_jasser_01.jsonahmose_vs_jasser_010—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_blade_categories/ahmose_vs_wu_ming.jsonahmose_vs_wu_ming0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_blade_categories/ahmose_wayne_vs_jasser.jsonahmose_wayne_vs_jasser0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_blade_categories/jasser_vs_ahmose_01.jsonjasser_vs_ahmose_010—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_blade_categories/jasser_vs_ahmose_wayne.jsonjasser_vs_ahmose_wayne0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_blade_categories/wu_ming_vs_ahmose.jsonwu_ming_vs_ahmose0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_bucket_interactions/ahmose_hendrik_vs_jasser.jsonahmose_hendrik_vs_jasser0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_bucket_interactions/ahmose_jasser_vs_jasser.jsonahmose_jasser_vs_jasser0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_bucket_interactions/ahmose_reina_vs_jasser.jsonahmose_reina_vs_jasser0—-0.12%-0.12%0.00%unchanged
testcases/emulator_verified/ahmose_bucket_interactions/ahmose_seo_yoon_vs_jasser.jsonahmose_seo_yoon_vs_jasser0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_bucket_interactions/ahmose_vs_gatot.jsonahmose_vs_gatot0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_bucket_interactions/ahmose_vs_wu_ming.jsonahmose_vs_wu_ming0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_bucket_interactions/wu_ming_vs_ahmose.jsonwu_ming_vs_ahmose0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_c2_discriminators/ahmose_renee_vs_jasser_01.jsonahmose_renee_vs_jasser_010—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_c2_discriminators/ahmose_renee_vs_jasser_02.jsonahmose_renee_vs_jasser_020—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_c2_discriminators/ahmose_vs_jasser_02.jsonahmose_vs_jasser_020—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_c2_discriminators/ahmose_vs_jasser_03.jsonahmose_vs_jasser_030—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_c2_discriminators/ahmose_vs_sonya_01.jsonahmose_vs_sonya_010—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_c2_discriminators/ahmose_vs_sonya_02.jsonahmose_vs_sonya_020—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_02.jsonjasser_vs_ahmose_020—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_03.jsonjasser_vs_ahmose_030—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_04.jsonjasser_vs_ahmose_040—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_05.jsonjasser_vs_ahmose_050—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_renee_01.jsonjasser_vs_ahmose_renee_010—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_renee_02.jsonjasser_vs_ahmose_renee_020—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_01.jsonsonya_vs_ahmose_010—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_02.jsonsonya_vs_ahmose_020—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_03.jsonsonya_vs_ahmose_030—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_04.jsonsonya_vs_ahmose_040—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_05.jsonsonya_vs_ahmose_050—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_06.jsonsonya_vs_ahmose_060—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_07.jsonsonya_vs_ahmose_070—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_gordon_renee/ahmose_bradley_vs_wuming.jsonahmose_bradley_vs_wuming0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_gordon_renee/ahmose_gordon_vs_jasser_control.jsonahmose_gordon_vs_jasser_control0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_gordon_renee/ahmose_gordon_wayne_pure_lancers_vs_jasser.jsonahmose_gordon_wayne_pure_lancers_vs_jasser0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_gordon_renee/ahmose_renee_vs_jasser_addition.jsonahmose_renee_vs_jasser_addition0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_gordon_renee/ahmose_renee_vs_jasser_control.jsonahmose_renee_vs_jasser_control0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_gordon_renee/ahmose_renee_vs_jasser_mark_kind.jsonahmose_renee_vs_jasser_mark_kind0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_gordon_renee/wuming_renee_vs_jasser.jsonwuming_renee_vs_jasser0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_isolation_20260925/ahmose_1000i_vs_wuming_1000i.jsonahmose_1000i_vs_wuming_1000i0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_isolation_20260925/ahmose_bradley_1000i_vs_3700i.jsonahmose_bradley_1000i_vs_3700i0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_no_infantry_240l_vs_125i.jsonahmose_no_infantry_240l_vs_125i0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_solo_nc.jsonahmose_solo0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_source_dies_001i_480l_vs_250i.jsonahmose_source_dies_001i_480l_vs_250i0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/alonso_attacker_600_all.jsonalonso_attacker_600_all0—-0.14%-0.14%0.00%unchanged
testcases/emulator_verified/alonso_attacker_900_inf_lancer.jsonalonso_attacker_900_inf_lancer0—0.05%0.05%0.00%unchanged
testcases/emulator_verified/alonso_attacker_900_inf_lancer.jsonalonso_attacker_900_inf_lancer1—0.02%0.02%0.00%unchanged
testcases/emulator_verified/alonso_minxxx_defender.jsonalonso_minxxx_defender0—0.32%0.32%0.00%unchanged
testcases/emulator_verified/alonso_norah_combo.jsonalonso_norah_combo0—-0.01%-0.01%0.00%unchanged
testcases/emulator_verified/alonso_norah_combo.jsonalonso_norah_combo1—-0.26%-0.26%0.00%unchanged
testcases/emulator_verified/alonso_norah_combo.jsonalonso_norah_combo2—-0.17%-0.17%0.00%unchanged
testcases/emulator_verified/alonso_solo.jsonalonso_solo0—2.95%2.95%0.00%unchanged
testcases/emulator_verified/alonso_solo_v2.jsonalonso_solo_v20—2.21%2.21%0.00%unchanged
testcases/emulator_verified/alonso_solo_v2.jsonalonso_solo_v21—-1.79%-1.79%0.00%unchanged
testcases/emulator_verified/alonso_vs_alonso.jsonalonso_vs_alonso0—-0.07%-0.07%0.00%unchanged
testcases/emulator_verified/alonso_vs_alonso_inf_lancer.jsonalonso_vs_alonso_inf_lancer0—-0.03%-0.03%0.00%unchanged
testcases/emulator_verified/alonso_wip_attacker.jsonalonso_wip_attacker0—0.03%0.03%0.00%unchanged
testcases/emulator_verified/army_term_precision_002i_003l.jsonarmy_term_precision_002i_003l0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/attdef_jasser_patrick_minxxx_attacks_nc.jsonattdef_jasser_patrick_minxxx_attacks0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/attdef_jasser_patrick_nc.jsonattdef_jasser_patrick0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/attdef_jasser_patrick_swap_nc.jsonattdef_jasser_patrick_swap0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/attdef_jasser_patrick_wip_attacks_nc.jsonattdef_jasser_patrick_wip_attacks0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/attdef_jasser_patrick_wip_attacks_nc.jsonattdef_jasser_patrick_wip_attacks1—0.00%0.00%0.00%unchanged
testcases/emulator_verified/attdef_test_wip_attacks_nc.jsonattdef_test_wip_attacks_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/bahiti_norah_combo_nc.jsonbahiti_norah_combo_nc0—-2.44%-2.44%0.00%unchanged
testcases/emulator_verified/bahiti_solo.jsonbahiti_solo0—0.52%0.52%0.00%unchanged
testcases/emulator_verified/bahiti_solo_nc.jsonbahiti_solo_nc0—0.05%0.05%0.00%unchanged
testcases/emulator_verified/damage_precision_325l_008i_325m.jsondamage_precision_325l_008i_325m0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/damage_precision_325l_009i_325m.jsondamage_precision_325l_009i_325m0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/damage_precision_875i_026i_875m.jsondamage_precision_875i_026i_875m0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/determinism_test_normal_nc.jsondeterminism_test_normal0+0.017%0.00%0.00%0.00%unchanged
testcases/emulator_verified/determinism_test_swapped_nc.jsondeterminism_test_swapped0-0.017%0.00%0.00%0.00%unchanged
testcases/emulator_verified/edith_ironclad_100i_vs_400m.jsonedith_ironclad_100i_vs_400m0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/flint_reina_zinman_combo.jsonflint_reina_zinman_combo0—-0.14%-0.14%0.00%unchanged
testcases/emulator_verified/flint_solo_nc.jsonflint_solo0+0.025%0.00%0.00%0.00%unchanged
testcases/emulator_verified/flint_zinman_combo_nc.jsonflint_zinman_combo_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gatot_sonya_hendrik_precise.jsonc2_27346931709781040—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gordon_interactions_20260925/gordon_wayne_1200l_vs_4800l.jsongordon_wayne_1200l_vs_4800l0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gordon_interactions_20260925/gordon_wayne_800l_vs_3200l.jsongordon_wayne_800l_vs_3200l0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gordon_interactions_20260925/wayne_control_1000l_vs_1700l.jsonwayne_control_1000l_vs_1700l0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gordon_isolation_20260925/gordon_solo_1329l_vs_5000l.jsongordon_solo_1329l_vs_5000l0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gordon_isolation_20260925/gordon_solo_180l_vs_700l.jsongordon_solo_180l_vs_700l0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gordon_isolation_20260925/gordon_solo_1860l_vs_7000l.jsongordon_solo_1860l_vs_7000l0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gordon_isolation_20260925/nohero_300l_vs_600l.jsonnohero_300l_vs_600l0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gordon_wayne_s1_extra_overlap_lvl2.jsongordon_wayne_s1_extra_overlap_nc0-0.025%0.00%0.00%0.00%unchanged
testcases/emulator_verified/gordon_wayne_s1_extra_overlap_nc.jsongordon_wayne_s1_extra_overlap_nc0+0.01%0.00%0.00%0.00%unchanged
testcases/emulator_verified/greg_deterrence_200i_vs_400m.jsongreg_deterrence_200i_vs_400m0—-0.12%-0.12%0.00%unchanged
testcases/emulator_verified/greg_mia_defender_current.jsongreg_mia_defender_current0—0.01%0.01%0.00%unchanged
testcases/emulator_verified/greg_mia_nohero_control_current.jsongreg_mia_nohero_control_current0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/greg_mia_nohero_control_current.jsongreg_mia_nohero_control_current1—0.00%0.00%0.00%unchanged
testcases/emulator_verified/greg_only_defender_current.jsongreg_only_defender_current0—0.02%0.02%0.00%unchanged
testcases/emulator_verified/greg_only_defender_current.jsongreg_only_defender_current1—0.03%0.03%0.00%unchanged
testcases/emulator_verified/greg_only_defender_current.jsongreg_only_defender_current2—0.00%0.00%0.00%unchanged
testcases/emulator_verified/greg_solo.jsongreg_solo0—0.16%0.16%0.00%unchanged
testcases/emulator_verified/gwen_blastmaster_cadence_800m_vs_1200i_100l_100m.jsongwen_blastmaster_cadence_800m_vs_1200i_100l_100m0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_blastmaster_fanout_800m_vs_600_each.jsongwen_blastmaster_fanout_800m_vs_600_each0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_blastmaster_replay_550m_vs_810i_45l_45m.jsongwen_blastmaster_replay_550m_vs_810i_45l_45m0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_blastmaster_small_timing_550m_vs_810i_45l_45m.jsongwen_blastmaster_small_timing_550m_vs_810i_45l_45m0-0.01%0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_c2_twenty/ahmose_gordon_gwen_vs_gatot_patrick_hendrik.jsonahmose_gordon_gwen_vs_gatot_patrick_hendrik0+0.005%-0.05%-0.05%0.00%unchanged
testcases/emulator_verified/gwen_c2_twenty/ahmose_renee_gwen_vs_sergey_patrick_hendrik.jsonahmose_renee_gwen_vs_sergey_patrick_hendrik0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_c2_twenty/edith_patrick_gwen_vs_ahmose_renee_bradley.jsonedith_patrick_gwen_vs_ahmose_renee_bradley0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_c2_twenty/flint_patrick_gwen_vs_ahmose_renee_jasser.jsonflint_patrick_gwen_vs_ahmose_renee_jasser0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_c2_twenty/gatot_renee_gwen_vs_sergey_sonya_wayne.jsongatot_renee_gwen_vs_sergey_sonya_wayne0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_c2_twenty/gatot_sonya_gwen_vs_edith_gordon_bradley.jsongatot_sonya_gwen_vs_edith_gordon_bradley0-0.005%0.05%0.05%0.00%unchanged
testcases/emulator_verified/gwen_c2_twenty/gwen_vs_ahmose.jsongwen_vs_ahmose0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_c2_twenty/gwen_vs_bradley.jsongwen_vs_bradley0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_c2_twenty/gwen_vs_flint_jessie_wayne.jsongwen_vs_flint_jessie_wayne0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_c2_twenty/gwen_vs_gatot.jsongwen_vs_gatot0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_c2_twenty/gwen_vs_hendrik.jsongwen_vs_hendrik0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_c2_twenty/gwen_vs_patrick.jsongwen_vs_patrick0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_c2_twenty/gwen_vs_renee.jsongwen_vs_renee0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_c2_twenty/gwen_vs_sergey.jsongwen_vs_sergey0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_c2_twenty/gwen_vs_sonya.jsongwen_vs_sonya0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_c2_twenty/gwen_vs_wu_ming_sonya_jasser.jsongwen_vs_wu_ming_sonya_jasser0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_c2_twenty/sergey_jessie_hendrik_vs_gwen.jsonsergey_jessie_hendrik_vs_gwen0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_c2_twenty/wu_ming_sonya_bradley_vs_gatot_patrick_gwen.jsonwu_ming_sonya_bradley_vs_gatot_patrick_gwen0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_c2_twenty/wu_ming_sonya_gwen_vs_edith_gordon_bradley.jsonwu_ming_sonya_gwen_vs_edith_gordon_bradley0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_gatot_cadence/gwen_60m_vs_gatot_400i.jsongwen_60m_vs_gatot_400i0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_gatot_cadence/gwen_65m_vs_gatot_200i.jsongwen_65m_vs_gatot_200i0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_norah_combo_nc.jsongwen_norah_combo_nc0—0.57%0.57%0.00%unchanged
testcases/emulator_verified/gwen_s1_probes/gwen_s1_a.jsongwen_s1_a0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_s1_probes/gwen_s1_b.jsongwen_s1_b0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_s1_probes/gwen_s1_c.jsongwen_s1_c0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_s1_probes/gwen_s1_d.jsongwen_s1_d0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_s1_probes/gwen_s1_e.jsongwen_s1_e0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_s2_probes/gwen_s2_calibration.jsongwen_s2_calibration0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_s2_probes/gwen_s2_curse_timing.jsongwen_s2_curse_timing0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_s2_probes/gwen_s2_first_trigger.jsongwen_s2_first_trigger0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_s2_probes/gwen_s2_mark_consumption.jsongwen_s2_mark_consumption0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_1.jsongwen_s2_source_10—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_2.jsongwen_s2_source_20—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_3.jsongwen_s2_source_30—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_4.jsongwen_s2_source_40—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_5.jsongwen_s2_source_50—0.00%0.00%0.00%unchanged
testcases/emulator_verified/gwen_solo_nc.jsongwen_solo0+0.025%0.00%0.00%0.00%unchanged
testcases/emulator_verified/hector_patrick_combo.jsonhector_patrick_combo0—-0.05%-0.05%0.00%unchanged
testcases/emulator_verified/hector_renee_wayne.jsonhector_renee_wayne0+0.05%-0.43%-0.43%0.00%unchanged
testcases/emulator_verified/hector_solo.jsonhector_solo0—0.16%0.16%0.00%unchanged
testcases/emulator_verified/hendrik_armor_lancer_scope.jsonhendrik_armor_lancer_scope0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/hendrik_dragons_heir_250m_vs_400i_100l_100m.jsonhendrik_dragons_heir_250m_vs_400i_100l_100m0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/hendrik_small_timing_250m_vs_150i_60l_60m.jsonhendrik_small_timing_250m_vs_150i_60l_60m0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/interaction_map/gordon_bradley_pure_lancers.jsongordon_bradley_pure_lancers0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/interaction_map/renee_bradley_pure_lancers.jsonrenee_bradley_pure_lancers0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/interaction_map/renee_jasser_pure_lancers.jsonrenee_jasser_pure_lancers0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/interaction_map/renee_wuming_pure_lancers.jsonrenee_wuming_pure_lancers0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/jasser_solo_nc.jsonjasser_solo0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/jessie_solo_nc.jsonjessie_solo0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ling_xue_solo_nc.jsonling_xue_solo0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ling_xue_solo_nc.jsonling_xue_solo1+0.05%0.00%0.00%0.00%unchanged
testcases/emulator_verified/logan_bahiti_combo_nc.jsonlogan_bahiti_combo_nc0—0.45%0.45%0.00%unchanged
testcases/emulator_verified/logan_gwen_combo_nc.jsonlogan_gwen_combo_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/logan_lynn_combo.jsonlogan_lynn_combo0—0.09%0.09%0.00%unchanged
testcases/emulator_verified/logan_lynn_combo.jsonlogan_lynn_combo1—0.02%0.02%0.00%unchanged
testcases/emulator_verified/logan_lynn_combo.jsonlogan_lynn_combo2—0.00%0.00%0.00%unchanged
testcases/emulator_verified/logan_reina_bahiti_combo_nc.jsonlogan_reina_bahiti_combo_nc0—-0.59%-0.59%0.00%unchanged
testcases/emulator_verified/logan_solo_minxxx_nc.jsonlogan_solo_minxxx0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/logan_solo_v2_nc.jsonlogan_solo_v20—0.00%0.00%0.00%unchanged
testcases/emulator_verified/lumak_bokan_solo_nc.jsonlumak_bokan_solo0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/lumak_bokan_solo_nc.jsonlumak_bokan_solo1+0.025%0.00%0.00%0.00%unchanged
testcases/emulator_verified/lynn_solo.jsonlynn_solo0—-0.08%-0.08%0.00%unchanged
testcases/emulator_verified/mia_only_defender_current.jsonmia_only_defender_current0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/mia_only_defender_current.jsonmia_only_defender_current1—-0.10%-0.10%0.00%unchanged
testcases/emulator_verified/mia_solo.jsonmia_solo0—-0.21%-0.21%0.00%unchanged
testcases/emulator_verified/mia_solo.jsonmia_solo1—0.32%0.32%0.00%unchanged
testcases/emulator_verified/mia_solo.jsonmia_solo2—-0.09%-0.09%0.00%unchanged
testcases/emulator_verified/mia_solo.jsonmia_solo3—0.04%0.04%0.00%unchanged
testcases/emulator_verified/mia_solo.jsonmia_solo4—0.34%0.34%0.00%unchanged
testcases/emulator_verified/molly_lynn_combo.jsonmolly_lynn_combo0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/molly_solo.jsonmolly_solo0—-0.09%-0.09%0.00%unchanged
testcases/emulator_verified/natalia_solo.jsonnatalia_solo0—-1.04%-1.04%0.00%unchanged
testcases/emulator_verified/nohero_controls/nohero_explicit_empty_100m_vs_100m.jsonnohero_explicit_empty_100m_vs_100m0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/norah_greg_combo.jsonnorah_greg_combo0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/norah_hector_zinman_combo.jsonnorah_hector_zinman_combo0—0.07%0.07%0.00%unchanged
testcases/emulator_verified/norah_s2_inf_only_A.jsonnorah_s2_inf_only_A0—-0.03%-0.03%0.00%unchanged
testcases/emulator_verified/norah_s2_inf_only_B_nc.jsonnorah_s2_inf_only_B0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/norah_s2_splash_A.jsonnorah_s2_splash_A0—0.35%0.35%0.00%unchanged
testcases/emulator_verified/norah_s2_splash_B_nc.jsonnorah_s2_splash_B0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/norah_s2_splash_C_nc.jsonnorah_s2_splash_C0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/norah_s2_splash_D.jsonnorah_s2_splash_D0—0.13%0.13%0.00%unchanged
testcases/emulator_verified/norah_solo.jsonnorah_solo0—-0.03%-0.03%0.00%unchanged
testcases/emulator_verified/norah_zinman_combo_nc.jsonnorah_zinman_combo_nc0—0.51%0.51%0.00%unchanged
testcases/emulator_verified/patrick_solo_nc.jsonpatrick_solo0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/philly_bahiti_combo.jsonphilly_bahiti_combo0—-0.13%-0.13%0.00%unchanged
testcases/emulator_verified/philly_bahiti_combo.jsonphilly_bahiti_combo1—0.21%0.21%0.00%unchanged
testcases/emulator_verified/philly_solo.jsonphilly_solo0—-0.01%-0.01%0.00%unchanged
testcases/emulator_verified/philly_solo.jsonphilly_solo1—0.04%0.04%0.00%unchanged
testcases/emulator_verified/plateau_boundary_700m_006i_700l.jsonplateau_boundary_700m_006i_700l0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/plateau_boundary_700m_007i_700l.jsonplateau_boundary_700m_007i_700l0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/plateau_boundary_700m_008i_700l.jsonplateau_boundary_700m_008i_700l0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/plateau_boundary_700m_009i_700l.jsonplateau_boundary_700m_009i_700l0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/reina_attacker_wip.jsonreina_attacker_wip0—0.01%0.01%0.00%unchanged
testcases/emulator_verified/reina_bahiti_combo_nc.jsonreina_bahiti_combo_nc0—1.07%1.07%0.00%unchanged
testcases/emulator_verified/reina_logan_combo_simulator.jsonreina_logan_combo_simulator0—0.18%0.18%0.00%unchanged
testcases/emulator_verified/reina_shadowblade_350l_vs_150i_50l_50m.jsonreina_shadowblade_350l_vs_150i_50l_50m0—0.25%0.25%0.00%unchanged
testcases/emulator_verified/reina_solo_wip.jsonreina_solo_wip0—-0.01%-0.01%0.00%unchanged
testcases/emulator_verified/renee_ahmose_damage_taken_overlap_attacker_nc.jsonrenee_ahmose_damage_taken_overlap_attacker_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/renee_charge_single_target_bucket_nc.jsonrenee_charge_single_target_bucket_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/renee_gwen_bucket_retry_nc.jsonrenee_gwen_bucket_retry_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/renee_hendrik_defense_bucket_nc.jsonrenee_hendrik_defense_bucket_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/renee_isolation_20260925/renee_mark_source_death.jsonrenee_mark_source_death0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/renee_s2_bucket_confirmations/gatot_shield_500_150.jsongatot_shield_500_1500—0.00%0.00%0.00%unchanged
testcases/emulator_verified/renee_s2_families/defense_gatot.jsondefense_gatot0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/renee_s2_families/defense_hendrik.jsondefense_hendrik0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/renee_s2_families/defense_logan.jsondefense_logan0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/renee_s2_families/defense_lumak.jsondefense_lumak0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/renee_s2_families/defense_patrick.jsondefense_patrick0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/renee_s2_families/defense_sergey.jsondefense_sergey0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/renee_s2_families/defense_wuming.jsondefense_wuming0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/renee_s2_families/renee_edith_hendrik.jsonrenee_edith_hendrik0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/renee_s2_families/renee_seo_charge.jsonrenee_seo_charge0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/renee_s2_families/skill_fleet_captain.jsonskill_fleet_captain0—-0.66%-0.66%0.00%unchanged
testcases/emulator_verified/renee_solo_nc.jsonrenee_solo_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/renee_wayne_mixed_510_vs_888.jsonrenee_wayne_mixed_510_vs_8880—-0.16%-0.16%0.00%unchanged
testcases/emulator_verified/renee_wu_ming_s1_damage_kind_nc.jsonrenee_wu_ming_s1_damage_kind_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/retarget_lancer_screen_nc.jsonretarget_lancer_screen_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/seo_yoon_solo_nc.jsonseo_yoon_solo0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/sergey_norah_zinman_combo_nc.jsonsergey_norah_zinman_combo_nc0—-0.74%-0.74%0.00%unchanged
testcases/emulator_verified/sergey_reina_combo_nc.jsonsergey_reina_combo_nc0—-0.03%-0.03%0.00%unchanged
testcases/emulator_verified/sergey_solo_nc.jsonsergey_solo0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/simple_001_nc.jsonsimple_0010—0.00%0.00%0.00%unchanged
testcases/emulator_verified/sonya_buffs_cadence_2200i_vs_800i_200l_800m.jsonsonya_buffs_cadence_2200i_vs_800i_200l_800m0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/troop_check_nc.jsontroop_check0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/type_triangle_A_nc.jsontype_triangle_A_nc0+0.05%0.00%0.00%0.00%unchanged
testcases/emulator_verified/type_triangle_B_nc.jsontype_triangle_B_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wayne_mixed_solo.jsonwayne_mixed_solo0—-0.31%-0.31%0.00%unchanged
testcases/emulator_verified/wayne_mixed_solo.jsonwayne_mixed_solo1—-1.79%-1.79%0.00%unchanged
testcases/emulator_verified/wayne_reina_combo.jsonwayne_reina_combo0—-0.32%-0.32%0.00%unchanged
testcases/emulator_verified/wayne_reina_wuming_combo.jsonwayne_reina_wuming_combo0—0.20%0.20%0.00%unchanged
testcases/emulator_verified/wayne_s1_solo.jsonwayne_s1_solo0—0.38%0.38%0.00%unchanged
testcases/emulator_verified/wayne_s2_solo.jsonwayne_s2_solo0—0.18%0.18%0.00%unchanged
testcases/emulator_verified/wos425_wip_nohero_control_nc.jsonwos425_wip_nohero_control_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wos425_wip_patrick_health_nc.jsonwos425_wip_patrick_health_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wos425_wip_philly_defense.jsonwos425_wip_philly_defense0—-0.03%-0.03%0.00%unchanged
testcases/emulator_verified/wos425_wip_sergey_damage_taken_nc.jsonwos425_wip_sergey_damage_taken_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wos425_wip_sergey_patrick_combo_nc.jsonwos425_wip_sergey_patrick_combo_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wos425_wip_sergey_philly_combo.jsonwos425_wip_sergey_philly_combo0—-0.05%-0.05%0.00%unchanged
testcases/emulator_verified/wos427_wip_sergey_philly_840.jsonwos427_wip_sergey_philly_8400—-0.93%-0.93%0.00%unchanged
testcases/emulator_verified/wos444_bradley_s3_all_troops_damage_control_nc.jsonwos444_bradley_s3_all_troops_damage_control_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wos444_bradley_s3_all_troops_damage_control_nc.jsonwos444_bradley_s3_all_troops_damage_control_nc1—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wos444_bradley_s3_all_troops_damage_hero_nc.jsonwos444_bradley_s3_all_troops_damage_hero_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wos444_edith_s11_mark_damage_taken_control_nc.jsonwos444_edith_s11_mark_damage_taken_control_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wos444_edith_s11_mark_damage_taken_control_nc.jsonwos444_edith_s11_mark_damage_taken_control_nc1—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wos444_edith_s11_mark_damage_taken_hero_nc.jsonwos444_edith_s11_mark_damage_taken_hero_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wos444_edith_s12_lancer_damage_dealt_control_nc.jsonwos444_edith_s12_lancer_damage_dealt_control_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wos444_edith_s12_lancer_damage_dealt_hero_nc.jsonwos444_edith_s12_lancer_damage_dealt_hero_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wos444_gordon_s22_all_enemy_damage_dealt_control_nc.jsonwos444_gordon_s22_all_enemy_damage_dealt_control_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wos444_gordon_s22_all_enemy_damage_dealt_control_nc.jsonwos444_gordon_s22_all_enemy_damage_dealt_control_nc1—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wos451_bradley_s21_lancer_damage_up_bucket.jsonwos451_bradley_s21_lancer_damage_up_bucket0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wos451_bradley_s21_lancer_damage_up_control.jsonwos451_bradley_s21_lancer_damage_up_control0-0.025%0.00%0.00%0.00%unchanged
testcases/emulator_verified/wos451_bradley_s22_infantry_damage_up_bucket.jsonwos451_bradley_s22_infantry_damage_up_bucket0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wos451_bradley_s22_infantry_damage_up_control.jsonwos451_bradley_s22_infantry_damage_up_control0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wos451_bradley_s3_all_damage_up_bucket.jsonwos451_bradley_s3_all_damage_up_bucket0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wos451_bradley_s3_all_damage_up_control.jsonwos451_bradley_s3_all_damage_up_control0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wos451_gordon_s11_lancer_damage_up_bucket.jsonwos451_gordon_s11_lancer_damage_up_bucket0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wos451_gordon_s11_lancer_damage_up_control.jsonwos451_gordon_s11_lancer_damage_up_control0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wos451_gordon_s21_lancer_damage_up_bucket.jsonwos451_gordon_s21_lancer_damage_up_bucket0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wos451_gordon_s21_lancer_damage_up_control.jsonwos451_gordon_s21_lancer_damage_up_control0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wu_ming_bradley_current_nc.jsonwu_ming_bradley_current_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wu_ming_marksman_vs_infantry_nc.jsonwu_ming_marksman_vs_infantry_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wu_ming_rounding_followup_nc.jsonwu_ming_rounding_followup_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wu_ming_s1_isolation_nc.jsonwu_ming_s1_isolation_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wu_ming_s2_isolation_nc.jsonwu_ming_s2_isolation_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wu_ming_solo_current_nc.jsonwu_ming_solo_current_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/wu_ming_solo_nc.jsonwu_ming_solo0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/zinman_solo_nc.jsonzinman_solo0—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s1-4900-vs-1000.jsons1-4900-vs-10000—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s1-4950-vs-1000.jsons1-4950-vs-10000+0.006%0.00%0.00%0.00%unchanged
testcases/gatot_verified/s1-5000-vs-1000.jsons1-5000-vs-10000+0.007%0.00%0.00%0.00%unchanged
testcases/gatot_verified/s1-5050-vs-1000.jsons1-5050-vs-10000+0.007%0.00%0.00%0.00%unchanged
testcases/gatot_verified/s10-bradley-1000-inf-125-marksman-vs-5000-inf.jsons10-bradley-1000-inf-125-marksman-vs-5000-inf0-0.019%0.00%0.00%0.00%unchanged
testcases/gatot_verified/s15.2-secondary-heroes-1000-inf-125-lancer-125-marksman.jsons15.2-secondary-heroes-1000-inf-125-lancer-125-marksman0-0.017%0.00%0.00%0.00%unchanged
testcases/gatot_verified/s15.3-1000-inf-0-lancer-1000-marksman.jsons15.3-1000-inf-0-lancer-1000-marksman0-0.037%0.00%0.00%0.00%unchanged
testcases/gatot_verified/s15.3-1000-inf-1000-lancer-0-marksman.jsons15.3-1000-inf-1000-lancer-0-marksman0-0.05%0.00%0.00%0.00%unchanged
testcases/gatot_verified/s15.3-1000-inf-1000-lancer-1000-marksman.jsons15.3-1000-inf-1000-lancer-1000-marksman0-0.032%0.00%0.00%0.00%unchanged
testcases/gatot_verified/s15.3-2000-inf-0-lancer-1000-marksman.jsons15.3-2000-inf-0-lancer-1000-marksman0-0.025%0.00%0.00%0.00%unchanged
testcases/gatot_verified/s15.3-2000-inf-1000-lancer-0-marksman.jsons15.3-2000-inf-1000-lancer-0-marksman0-0.029%0.00%0.00%0.00%unchanged
testcases/gatot_verified/s16-1500-inf-1500-marksman-vs-1000-inf.jsons16-1500-inf-1500-marksman-vs-1000-inf0—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s16-2000-inf-2000-marksman-vs-1000-inf.jsons16-2000-inf-2000-marksman-vs-1000-inf0—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s16-2000-inf-2000-marksman-vs-5000-inf.jsons16-2000-inf-2000-marksman-vs-5000-inf0+0.008%0.00%0.00%0.00%unchanged
testcases/gatot_verified/s16-7500-inf-vs-5000-inf.jsons16-7500-inf-vs-5000-inf0+0.013%0.00%0.00%0.00%unchanged
testcases/gatot_verified/s2-health-1.1-5150-vs-1000.jsons2-health-1.1-5150-vs-10000—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s25-1000-inf-3000-marksman-vs-5000-inf.jsons25-1000-inf-3000-marksman-vs-5000-inf0+0.017%0.00%0.00%0.00%unchanged
testcases/gatot_verified/s25-2000-inf-2000-marksman-vs-2000-inf.jsons25-2000-inf-2000-marksman-vs-2000-inf0+0.05%0.00%0.00%0.00%unchanged
testcases/gatot_verified/s25-2000-inf-2000-marksman-vs-3000-inf.jsons25-2000-inf-2000-marksman-vs-3000-inf0—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s25-2000-inf-2000-marksman-vs-4000-inf.jsons25-2000-inf-2000-marksman-vs-4000-inf0—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s25-3000-inf-1000-marksman-vs-5000-inf.jsons25-3000-inf-1000-marksman-vs-5000-inf0+0.01%0.00%0.00%0.00%unchanged
testcases/gatot_verified/s31-current-2000-inf-2000-marksman-vs-5000-inf.jsons31-current-2000-inf-2000-marksman-vs-5000-inf0+0.025%0.00%0.00%0.00%unchanged
testcases/gatot_verified/s32-current-750-marksman-wayne-vs-3000-inf.jsons32-current-750-marksman-wayne-vs-3000-inf0—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s33-current-500-marksman-wayne3-vs-5000-gatot3-inf.jsons33-current-500-marksman-wayne3-vs-5000-gatot3-inf0—0.09%0.09%0.00%unchanged
testcases/gatot_verified/s34-current-2100-lancer-2100-marksman-vs-1100-gatot3-inf.jsons34-current-2100-lancer-2100-marksman-vs-1100-gatot3-inf0—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s35-current-3000-lancer-1000-marksman-vs-1100-gatot3-inf.jsons35-current-3000-lancer-1000-marksman-vs-1100-gatot3-inf0—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s36-current-600-lancer-600-marksman-vs-1100-gatot3-inf.jsons36-current-600-lancer-600-marksman-vs-1100-gatot3-inf0—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s37-current-bilateral-gatot-2000-inf-2000-marksman-vs-5000-inf.jsons37-current-bilateral-gatot-2000-inf-2000-marksman-vs-5000-inf0—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s38-current-bilateral-gatot-800-inf-800-marksman-vs-1100-inf.jsons38-current-bilateral-gatot-800-inf-800-marksman-vs-1100-inf0—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s39-1000-lancer-400-marksman-vs-500-gatot3-inf.jsons39-1000-lancer-400-marksman-vs-500-gatot3-inf0—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s4-fc9-gatot-10000.jsons4-fc9-gatot-100000—0.01%0.01%0.00%unchanged
testcases/gatot_verified/s4-fc9-gatot-20000.jsons4-fc9-gatot-200000—0.01%0.01%0.00%unchanged
testcases/gatot_verified/s4-fc9-gatot-40000.jsons4-fc9-gatot-400000—0.15%0.15%0.00%unchanged
testcases/gatot_verified/s4-fc9-gatot-90000.jsons4-fc9-gatot-900000—-0.09%-0.09%0.00%unchanged
testcases/gatot_verified/s40-2000-inf-1000-marksman-vs-5000-inf.jsons40-2000-inf-1000-marksman-vs-5000-inf0—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s41-300-inf-200-lancer-600-marksman-vs-500-inf.jsons41-300-inf-200-lancer-600-marksman-vs-500-inf0—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s42-current-wayne-skill-shield-scope-200-inf-vs-450-inf.jsons42-current-wayne-skill-shield-scope-200-inf-vs-450-inf0—-0.12%-0.12%0.00%unchanged
testcases/gatot_verified/s43-current-reciprocal-shield-cap-450-inf-vs-200-inf.jsons43-current-reciprocal-shield-cap-450-inf-vs-200-inf0—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s44-current-reciprocal-shield-cap-2500-inf-vs-500-inf.jsons44-current-reciprocal-shield-cap-2500-inf-vs-500-inf0—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s5-fc9-gatot-lumak-20000.jsons5-fc9-gatot-lumak-200000—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s5-fc9-gatot-lumak-40000.jsons5-fc9-gatot-lumak-400000—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s5-fc9-gatot-lumak-90000.jsons5-fc9-gatot-lumak-900000—0.01%0.01%0.00%unchanged
testcases/gatot_verified/s7-10000-t6-marksmen-vs-one-t1-fc10-infantry.jsons7-10000-t6-marksmen-vs-one-t1-fc10-infantry0—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s8-10000-t9-marksmen-vs-one-t1-fc10-infantry.jsons8-10000-t9-marksmen-vs-one-t1-fc10-infantry0—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s8-11548-t9-marksmen-vs-one-t1-fc10-infantry.jsons8-11548-t9-marksmen-vs-one-t1-fc10-infantry0-0.05%0.68%0.68%0.00%unchanged
testcases/gatot_verified/s8-11791-t9-marksmen-vs-one-t1-fc10-infantry.jsons8-11791-t9-marksmen-vs-one-t1-fc10-infantry0—0.48%0.48%0.00%unchanged
testcases/gatot_verified/s8-12000-t9-marksmen-vs-one-t1-fc10-infantry.jsons8-12000-t9-marksmen-vs-one-t1-fc10-infantry0—0.31%0.31%0.00%unchanged
testcases/gatot_verified/s8-20000-t9-marksmen-vs-one-t1-fc10-infantry.jsons8-20000-t9-marksmen-vs-one-t1-fc10-infantry0—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s8-25000-t9-marksmen-vs-one-t1-fc10-infantry.jsons8-25000-t9-marksmen-vs-one-t1-fc10-infantry0-0.05%0.14%0.14%0.00%unchanged
testcases/gatot_verified/s8-30116-t9-marksmen-vs-one-t1-fc10-infantry.jsons8-30116-t9-marksmen-vs-one-t1-fc10-infantry0—0.02%0.02%0.00%unchanged
testcases/gatot_verified/s8-30139-t9-marksmen-vs-one-t1-fc10-infantry.jsons8-30139-t9-marksmen-vs-one-t1-fc10-infantry0—-0.02%-0.02%0.00%unchanged
testcases/gatot_verified/s8-30179-t9-marksmen-vs-one-t1-fc10-infantry.jsons8-30179-t9-marksmen-vs-one-t1-fc10-infantry0—0.04%0.04%0.00%unchanged
testcases/gatot_verified/s8-30205-t9-marksmen-vs-one-t1-fc10-infantry.jsons8-30205-t9-marksmen-vs-one-t1-fc10-infantry0—-0.01%-0.01%0.00%unchanged
testcases/gatot_verified/s8-30228-t9-marksmen-vs-one-t1-fc10-infantry.jsons8-30228-t9-marksmen-vs-one-t1-fc10-infantry0—-0.02%-0.02%0.00%unchanged
testcases/gatot_verified/s8-33427-t9-marksmen-vs-one-t1-fc10-infantry.jsons8-33427-t9-marksmen-vs-one-t1-fc10-infantry0—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s8-33452-t9-marksmen-vs-one-t1-fc10-infantry.jsons8-33452-t9-marksmen-vs-one-t1-fc10-infantry0—0.00%0.00%0.00%unchanged
testcases/gatot_verified/s8-33481-t9-marksmen-vs-one-t1-fc10-infantry.jsons8-33481-t9-marksmen-vs-one-t1-fc10-infantry0—0.01%0.01%0.00%unchanged
testcases/gatot_verified/s8-33510-t9-marksmen-vs-one-t1-fc10-infantry.jsons8-33510-t9-marksmen-vs-one-t1-fc10-infantry0—0.01%0.01%0.00%unchanged
testcases/gatot_verified/s9-1500-t9-lancers-vs-146-t1-fc10-lancers.jsons9-1500-t9-lancers-vs-146-t1-fc10-lancers0—-0.01%-0.01%0.00%unchanged
testcases/gatot_verified/s9-1950-t9-lancers-vs-146-t1-fc10-lancers.jsons9-1950-t9-lancers-vs-146-t1-fc10-lancers0—-0.09%-0.09%0.00%unchanged
testcases/gatot_verified/s9-2100-t9-lancers-vs-146-t1-fc10-lancers.jsons9-2100-t9-lancers-vs-146-t1-fc10-lancers0—0.52%0.52%0.00%unchanged
testcases/gatot_verified/s9-2200-t9-lancers-vs-146-t1-fc10-lancers.jsons9-2200-t9-lancers-vs-146-t1-fc10-lancers0—2.49%2.49%0.00%unchanged
testcases/gatot_verified/s9-2201-t9-lancers-vs-146-t1-fc10-lancers.jsons9-2201-t9-lancers-vs-146-t1-fc10-lancers0—-0.37%-0.37%0.00%unchanged
testcases/gatot_verified/s9-3000-t9-lancers-vs-146-t1-fc10-lancers.jsons9-3000-t9-lancers-vs-146-t1-fc10-lancers0—0.22%0.22%0.00%unchanged
testcases/heroes_unittests/Hector_tc.jsonhector_tc_10—-0.01%-0.01%0.00%unchanged
testcases/heroes_unittests/Jessie_tc_nc.jsonjessie_tc_10—0.00%0.00%0.00%unchanged
testcases/heroes_unittests/Jessie_tc_nc.jsonjessie_tc_21—0.00%0.00%0.00%unchanged
testcases/heroes_unittests/Jessie_tc_nc.jsonjessie_tc_32—0.00%0.00%0.00%unchanged
testcases/heroes_unittests/Jessie_tc_nc.jsonjessie_tc_43+0.005%0.00%0.00%0.00%unchanged
testcases/heroes_unittests/Jessie_tc_nc.jsonjessie_tc_54—0.00%0.00%0.00%unchanged
testcases/heroes_unittests/Jessie_tc_nc.jsonjessie_tc_65—0.00%0.00%0.00%unchanged
testcases/heroes_unittests/Jessie_tc_nc.jsonjessie_tc_76—0.00%0.00%0.00%unchanged
testcases/heroes_unittests/Jessie_tc_nc.jsonjessie_tc_87—0.00%0.00%0.00%unchanged
testcases/heroes_unittests/Mia_tc.jsonmia_tc_10—0.20%0.20%0.00%unchanged
testcases/heroes_unittests/Mia_tc.jsonmia_tc_21—-0.23%-0.23%0.00%unchanged
testcases/heroes_unittests/Mia_tc.jsonmia_tc_32—0.00%0.00%0.00%unchanged
testcases/heroes_unittests/Mia_tc.jsonmia_tc_43—-0.34%-0.34%0.00%unchanged
testcases/heroes_unittests/Mia_tc.jsonmia_tc_54—-0.05%-0.05%0.00%unchanged
testcases/heroes_unittests/Molly_tc.jsonmolly_tc_10—0.00%0.00%0.00%unchanged
testcases/heroes_unittests/Molly_tc.jsonmolly_tc_21—0.00%0.00%0.00%unchanged
testcases/heroes_unittests/Molly_tc.jsonmolly_tc_32—0.00%0.00%0.00%unchanged
testcases/heroes_unittests/Molly_tc.jsonmolly_tc_43—0.08%0.08%0.00%unchanged
testcases/heroes_unittests/Molly_tc.jsonmolly_tc_54—-0.02%-0.02%0.00%unchanged
testcases/heroes_unittests/Reina_tc.jsonreina_tc_10—0.00%0.00%0.00%unchanged
testcases/heroes_unittests/Reina_tc.jsonreina_tc_21—0.00%0.00%0.00%unchanged
testcases/heroes_unittests/Reina_tc.jsonreina_tc_42—-0.57%-0.57%0.00%unchanged
testcases/heroes_unittests/Reina_tc.jsonreina_tc_53—0.01%0.01%0.00%unchanged
testcases/heroes_unittests/Seo-yoon_tc_nc.jsonseoyoon_tc_10—0.00%0.00%0.00%unchanged
testcases/heroes_unittests/Seo-yoon_tc_nc.jsonseoyoon_tc_32—0.00%0.00%0.00%unchanged
testcases/heroes_unittests/Seo-yoon_tc_nc.jsonseoyoon_tc_43—0.00%0.00%0.00%unchanged
testcases/heroes_unittests/Seo-yoon_tc_nc.jsonseoyoon_tc_54—0.00%0.00%0.00%unchanged
testcases/heroes_unittests/Seo-yoon_tc_nc.jsonseoyoon_tc_61—0.00%0.00%0.00%unchanged
testcases/heroes_unittests/Seo-yoon_tc_nc.jsonseoyoon_tc_65—0.00%0.00%0.00%unchanged
testcases/heroes_unittests/Seo-yoon_tc_nc.jsonseoyoon_tc_76—0.00%0.00%0.00%unchanged
testcases/heroes_unittests/Zinman_tc.jsonzinman_tc_10—0.00%0.00%0.00%unchanged
testcases/heroes_unittests/Zinman_tc.jsonzinman_tc_21—0.00%0.00%0.00%unchanged
testcases/heroes_unittests/Zinman_tc.jsonzinman_tc_32—0.00%0.00%0.00%unchanged
testcases/emulator_verified/ahmose_gordon_fresh_20260925/ahmose_gordon_75i300l_vs_wuming_225i75l75m.jsonahmose_gordon_75i300l_vs_wuming_225i75l75m0——0.00%—added
testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_150l_vs_wuming_60i20l20m.jsongordon_150l_vs_wuming_60i20l20m0——0.00%—added
testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_300l_vs_1000i.jsongordon_300l_vs_1000i0——0.00%—added
testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_300l_vs_1200m.jsongordon_300l_vs_1200m0——0.00%—added
testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_5000l_vs_100l.jsongordon_5000l_vs_100l0——0.00%—added
testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_5000l_vs_300l.jsongordon_5000l_vs_300l0——0.00%—added
testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_50i200l_vs_wuming_150i50l50m.jsongordon_50i200l_vs_wuming_150i50l50m0——0.00%—added
testcases/emulator_verified/extra_damage_source_death_20260926/calibration_gwen.jsoncalibration_gwen0——0.00%—added
testcases/emulator_verified/extra_damage_source_death_20260926/calibration_renee.jsoncalibration_renee0——0.00%—added
testcases/emulator_verified/extra_damage_source_death_20260926/calibration_renee_gwen.jsoncalibration_renee_gwen0——0.00%—added
testcases/emulator_verified/extra_damage_source_death_20260926/gwen_r9_cancellation_control.jsongwen_r9_cancellation_control0——0.00%—added
testcases/emulator_verified/extra_damage_source_death_20260926/gwen_r9_cancellation_death.jsongwen_r9_cancellation_death0——0.00%—added
testcases/emulator_verified/extra_damage_source_death_20260926/gwen_r9_delayed_control.jsongwen_r9_delayed_control0——0.00%—added
testcases/emulator_verified/extra_damage_source_death_20260926/gwen_r9_placement_control.jsongwen_r9_placement_control0——0.00%—added
testcases/emulator_verified/extra_damage_source_death_20260926/gwen_r9_placement_death.jsongwen_r9_placement_death0——0.00%—added
testcases/emulator_verified/extra_damage_source_death_20260926/renee_gwen_source_death.jsonrenee_gwen_source_death0——0.00%—added
testcases/emulator_verified/extra_damage_source_death_20260926/renee_gwen_survival_control.jsonrenee_gwen_survival_control0——0.00%—added
testcases/emulator_verified/extra_damage_source_death_20260926/renee_source_death.jsonrenee_source_death0——0.00%—added
testcases/emulator_verified/extra_damage_source_death_20260926/renee_survival_control.jsonrenee_survival_control0——0.00%—added
testcases/emulator_verified/gatot_fresh_20260925/gatot_mixed_gordon_gwen.jsongatot_mixed_gordon_gwen0——0.00%—added
testcases/emulator_verified/gatot_fresh_20260925/gatot_mixed_without_gordon.jsongatot_mixed_without_gordon0——0.00%—added
testcases/emulator_verified/gatot_fresh_20260925/gatot_mixed_without_gwen.jsongatot_mixed_without_gwen0——0.00%—added
testcases/emulator_verified/gordon_gwen_t9_probe_20260929/gg_1000i_1000l_vs_20000t9.jsongg_1000i_1000l_vs_20000t90——0.00%—added
testcases/emulator_verified/gordon_gwen_t9_probe_20260929/gg_1000l_vs_20000t9.jsongg_1000l_vs_20000t90——0.00%—added
testcases/emulator_verified/gordon_gwen_t9_probe_20260929/gg_1000l_vs_20000t9l_10000t9m.jsongg_1000l_vs_20000t9l_10000t9m0——0.08%—added
testcases/emulator_verified/gordon_gwen_t9_probe_20260929/gg_60i_1000l_vs_20000t9.jsongg_60i_1000l_vs_20000t90——0.00%—added
testcases/emulator_verified/gordon_gwen_t9_probe_20260929/gg_700i_1000l_vs_20000t9.jsongg_700i_1000l_vs_20000t90——0.00%—added
testcases/emulator_verified/gordon_gwen_t9_probe_20260929/go_1000l_vs_20000t9.jsongo_1000l_vs_20000t90——0.00%—added
testcases/emulator_verified/gordon_gwen_t9_probe_20260929/go_1000l_vs_20000t9l_10000t9m.jsongo_1000l_vs_20000t9l_10000t9m0——0.01%—added
testcases/emulator_verified/gordon_gwen_t9_probe_20260929/go_60i_1000l_vs_20000t9.jsongo_60i_1000l_vs_20000t90——0.00%—added
testcases/emulator_verified/gwen_renee_fresh_20260926/wip_1000l_1m_vs_3500l.jsonwip_1000l_1m_vs_3500l0——0.00%—added
testcases/emulator_verified/gwen_renee_fresh_20260926/wip_1000l_vs_3500l.jsonwip_1000l_vs_3500l0——0.00%—added
testcases/emulator_verified/gwen_renee_fresh_20260926/wip_4000l_vs_300l.jsonwip_4000l_vs_300l0——0.00%—added
testcases/emulator_verified/gwen_renee_fresh_20260926/wip_4000l_vs_400l.jsonwip_4000l_vs_400l0——0.00%—added
testcases/emulator_verified/gwen_renee_fresh_20260926/wip_4000l_vs_600l.jsonwip_4000l_vs_600l0——0.00%—added
testcases/emulator_verified/gwen_renee_skill_isolation/gwen_only_before_s2.jsongwen_only_before_s20——0.00%—added
testcases/emulator_verified/gwen_renee_skill_isolation/gwen_renee_after_s2.jsongwen_renee_after_s20——0.00%—added
testcases/emulator_verified/gwen_renee_skill_isolation/gwen_renee_before_s2.jsongwen_renee_before_s20——0.00%—added
testcases/emulator_verified/gwen_renee_skill_isolation/gwen_renee_first_s2.jsongwen_renee_first_s20——0.00%—added
testcases/emulator_verified/gwen_renee_skill_isolation/gwen_renee_mixed_after_s2.jsongwen_renee_mixed_after_s20——0.00%—added
testcases/emulator_verified/gwen_renee_skill_isolation/gwen_renee_mixed_before_s2.jsongwen_renee_mixed_before_s20——0.00%—added
testcases/emulator_verified/gwen_renee_skill_isolation/renee_only_short.jsonrenee_only_short0——0.00%—added
testcases/emulator_verified/gwen_renee_wall_20260926/renee_500l_vs_gatot_300i.jsonrenee_500l_vs_gatot_300i0——0.00%—added
testcases/emulator_verified/gwen_renee_wall_20260926/renee_gwen_500l_vs_gatot_300i.jsonrenee_gwen_500l_vs_gatot_300i0——0.00%—added
testcases/emulator_verified/model_checks_20260929/c10_gwen_300l_vs_31000t9l.jsonc10_gwen_300l_vs_31000t9l0——0.00%—added
testcases/emulator_verified/model_checks_20260929/c11a_bradley_norah_1500l_vs_2000l_3000m.jsonc11a_bradley_norah_1500l_vs_2000l_3000m0——9.34%—added
testcases/emulator_verified/model_checks_20260929/c11b_bradley_norah_1500l_vs_2000l_3000m.jsonc11b_bradley_norah_1500l_vs_2000l_3000m0——-9.48%—added
testcases/emulator_verified/model_checks_20260929/c1a_bradley_norah_1500l_vs_2000i_3000m.jsonc1a_bradley_norah_1500l_vs_2000i_3000m0——-1.43%—added
testcases/emulator_verified/model_checks_20260929/c1b_bradley_norah_1500l_vs_2000i_3000m.jsonc1b_bradley_norah_1500l_vs_2000i_3000m0——-0.97%—added
testcases/emulator_verified/model_checks_20260929/c2_bradley_800m_vs_1000i_1200m.jsonc2_bradley_800m_vs_1000i_1200m0——0.00%—added
testcases/emulator_verified/model_checks_20260929/c3_renee_gwen_1000l_vs_8000t9m.jsonc3_renee_gwen_1000l_vs_8000t9m0——-0.81%—added
testcases/emulator_verified/model_checks_20260929/c4_renee_1000l_vs_8000t9m.jsonc4_renee_1000l_vs_8000t9m0——0.00%—added
testcases/emulator_verified/model_checks_20260929/c5_gordon_1000l_vs_gatot_3000i.jsonc5_gordon_1000l_vs_gatot_3000i0——0.00%—added
testcases/emulator_verified/model_checks_20260929/c6_gordon_1000l_vs_10000t9m.jsonc6_gordon_1000l_vs_10000t9m0——-0.35%—added
testcases/emulator_verified/model_checks_20260929/c7_noheroes_1000l_vs_10000t9m.jsonc7_noheroes_1000l_vs_10000t9m0——0.10%—added
testcases/emulator_verified/model_checks_20260929/c8_gordon_gwen_300l_vs_31000t9l.jsonc8_gordon_gwen_300l_vs_31000t9l0——0.00%—added
testcases/emulator_verified/model_checks_20260929/c9_gordon_300l_vs_31000t9l.jsonc9_gordon_300l_vs_31000t9l0——0.00%—added
testcases/emulator_verified/remaining_parity_20260929/natalia_mixed_control.jsonnatalia_mixed_control0——-0.10%—added
testcases/emulator_verified/remaining_parity_20260929/six_hero_mixed_control.jsonsix_hero_mixed_control0——-0.05%—added
testcases/emulator_verified/renee_benign_controls_20260929/renee_jasser_mixed.jsonrenee_jasser_mixed0——0.00%—added
testcases/emulator_verified/renee_benign_controls_20260929/renee_jasser_pure.jsonrenee_jasser_pure0——0.00%—added
testcases/emulator_verified/renee_benign_controls_20260929/renee_sergey_mixed.jsonrenee_sergey_mixed0——0.00%—added
testcases/emulator_verified/renee_benign_controls_20260929/renee_sergey_pure.jsonrenee_sergey_pure0——0.00%—added
testcases/emulator_verified/renee_benign_controls_20260929/renee_solo_mixed.jsonrenee_solo_mixed0——0.00%—added
testcases/emulator_verified/renee_benign_controls_20260929/renee_solo_pure.jsonrenee_solo_pure0——0.00%—added
testcases/emulator_verified/renee_gwen_lancer_isolation_20260927/no_infantry_vs_infantry.jsonno_infantry_vs_infantry0——0.00%—added
testcases/emulator_verified/renee_gwen_lancer_isolation_20260927/no_infantry_vs_lancer.jsonno_infantry_vs_lancer0——0.00%—added
testcases/emulator_verified/renee_gwen_lancer_isolation_20260927/no_marksmen_vs_infantry.jsonno_marksmen_vs_infantry0——0.00%—added
testcases/emulator_verified/renee_gwen_lancer_isolation_20260927/no_marksmen_vs_lancer.jsonno_marksmen_vs_lancer0——0.00%—added
testcases/emulator_verified/renee_gwen_lancer_isolation_20260927/only_lancers_vs_infantry.jsononly_lancers_vs_infantry0——0.00%—added
testcases/emulator_verified/renee_gwen_lancer_isolation_20260927/only_lancers_vs_lancer.jsononly_lancers_vs_lancer0——0.00%—added
testcases/emulator_verified/renee_gwen_no_lancers_20260927/no_lancers_1.jsonno_lancers_10——0.00%—added
testcases/emulator_verified/renee_gwen_no_lancers_20260927/no_lancers_2.jsonno_lancers_20——0.00%—added
testcases/emulator_verified/renee_gwen_no_lancers_20260927/no_lancers_3.jsonno_lancers_30——0.00%—added
testcases/emulator_verified/renee_gwen_no_lancers_20260927/no_lancers_4.jsonno_lancers_40——0.00%—added
testcases/emulator_verified/renee_gwen_precision_20260925/gwen_only_1000l_vs_3000l.jsongwen_only_1000l_vs_3000l0——0.00%—added
testcases/emulator_verified/renee_gwen_precision_20260925/renee_gwen_1000l_vs_7800l.jsonrenee_gwen_1000l_vs_7800l0——0.00%—added
testcases/emulator_verified/renee_gwen_precision_20260925/renee_only_1000l_vs_7800l.jsonrenee_only_1000l_vs_7800l0——0.00%—added
testcases/emulator_verified/renee_gwen_single_lines_20260927/only_infantry.jsononly_infantry0——0.00%—added
testcases/emulator_verified/renee_gwen_single_lines_20260927/only_marksman.jsononly_marksman0——0.00%—added
testcases/emulator_verified/renee_gwen_t9_probe_20260929/g_1000l_vs_20000t9.jsong_1000l_vs_20000t90——0.00%—added
testcases/emulator_verified/renee_gwen_t9_probe_20260929/g_60i_1000l_vs_20000t9.jsong_60i_1000l_vs_20000t90——0.00%—added
testcases/emulator_verified/renee_gwen_t9_probe_20260929/r_1000l_vs_20000t9.jsonr_1000l_vs_20000t90——0.00%—added
testcases/emulator_verified/renee_gwen_t9_probe_20260929/r_60i_1000l_vs_20000t9.jsonr_60i_1000l_vs_20000t90——0.00%—added
testcases/emulator_verified/renee_gwen_t9_probe_20260929/rg_1000l_vs_20000t9.jsonrg_1000l_vs_20000t90——0.00%—added
testcases/emulator_verified/renee_gwen_t9_probe_20260929/rg_200i_1000l_vs_20000t9.jsonrg_200i_1000l_vs_20000t90——0.00%—added
testcases/emulator_verified/renee_gwen_t9_probe_20260929/rg_30i_1000l_vs_20000t9.jsonrg_30i_1000l_vs_20000t90——0.00%—added
testcases/emulator_verified/renee_gwen_t9_probe_20260929/rg_400i_1000l_vs_20000t9.jsonrg_400i_1000l_vs_20000t90——0.00%—added
testcases/emulator_verified/renee_gwen_t9_probe_20260929/rg_60i_1000l_vs_20000t9.jsonrg_60i_1000l_vs_20000t90——0.00%—added
testcases/emulator_verified/renee_mixed_partners_20260929/renee_jasser_balanced.jsonrenee_jasser_balanced0——0.00%—added
testcases/emulator_verified/renee_mixed_partners_20260929/renee_jasser_lancer_heavy.jsonrenee_jasser_lancer_heavy0——0.00%—added
testcases/emulator_verified/renee_mixed_partners_20260929/renee_seoyoon_balanced.jsonrenee_seoyoon_balanced0——0.00%—added
testcases/emulator_verified/renee_mixed_partners_20260929/renee_seoyoon_lancer_heavy.jsonrenee_seoyoon_lancer_heavy0——0.00%—added
testcases/emulator_verified/renee_mixed_partners_20260929/renee_sergey_balanced.jsonrenee_sergey_balanced0——0.00%—added
testcases/emulator_verified/renee_mixed_partners_20260929/renee_sergey_lancer_heavy.jsonrenee_sergey_lancer_heavy0——0.00%—added
testcases/emulator_verified/renee_mixed_partners_20260929/renee_solo_balanced.jsonrenee_solo_balanced0——0.00%—added
testcases/emulator_verified/renee_mixed_partners_20260929/renee_solo_lancer_heavy.jsonrenee_solo_lancer_heavy0——0.00%—added
testcases/emulator_verified/renee_shield_saturation_20260926/renee_100l_vs_gatot_100i.jsonrenee_100l_vs_gatot_100i0——0.00%—added
testcases/emulator_verified/renee_shield_saturation_20260926/renee_100l_vs_gatot_50i.jsonrenee_100l_vs_gatot_50i0——0.00%—added
testcases/emulator_verified/renee_stun_20260925/renee_300l_vs_sonya_250l.jsonrenee_300l_vs_sonya_250l0——0.00%—added
testcases/emulator_verified/renee_stun_20260925/renee_400l_vs_sonya_400l.jsonrenee_400l_vs_sonya_400l0——0.00%—added
testcases/emulator_verified/renee_turn_boundary_20260927/calibration_renee.jsoncalibration_renee0——0.00%—added
testcases/emulator_verified/renee_turn_boundary_20260927/calibration_renee_gwen.jsoncalibration_renee_gwen0——0.00%—added
testcases/emulator_verified/renee_turn_boundary_20260927/combined_infantry_probe.jsoncombined_infantry_probe0——0.00%—added
testcases/emulator_verified/renee_turn_boundary_20260927/combined_lancer_probe.jsoncombined_lancer_probe0——0.00%—added
testcases/emulator_verified/renee_turn_boundary_20260927/pure_renee_matched_control.jsonpure_renee_matched_control0——0.00%—added
testcases/emulator_verified/scheduled_skill_stun_20260925/sonya_150l_vs_hendrik_250m.jsonsonya_150l_vs_hendrik_250m0——0.00%—added
testcases/emulator_verified/scheduled_skill_stun_20260925/sonya_600l_vs_hendrik_1200m.jsonsonya_600l_vs_hendrik_1200m0——0.00%—added
testcases/emulator_verified/scheduled_skill_stun_20260925/wayne_500i_vs_sonya_150l.jsonwayne_500i_vs_sonya_150l0——0.46%—added
testcases/emulator_verified/skill_buckets_20260928/control_edith.jsoncontrol_edith0——0.00%—added
testcases/emulator_verified/skill_buckets_20260928/control_gatot.jsoncontrol_gatot0——0.00%—added
testcases/emulator_verified/skill_buckets_20260928/control_jessie.jsoncontrol_jessie0——0.00%—added
testcases/emulator_verified/skill_buckets_20260928/control_ling.jsoncontrol_ling0——0.00%—added
testcases/emulator_verified/skill_buckets_20260928/control_lumak.jsoncontrol_lumak0——0.00%—added
testcases/emulator_verified/skill_buckets_20260928/control_none.jsoncontrol_none0——0.00%—added
testcases/emulator_verified/skill_buckets_20260928/edith_jessie.jsonedith_jessie0——0.00%—added
testcases/emulator_verified/skill_buckets_20260928/edith_ling.jsonedith_ling0——0.00%—added
testcases/emulator_verified/skill_buckets_20260928/edith_lumak.jsonedith_lumak0——0.00%—added
testcases/emulator_verified/skill_buckets_20260928/gatot_jessie.jsongatot_jessie0——0.00%—added
testcases/emulator_verified/skill_buckets_20260928/gatot_ling.jsongatot_ling0——0.00%—added
testcases/emulator_verified/skill_buckets_20260928/gatot_lumak.jsongatot_lumak0——0.00%—added
testcases/emulator_verified/skill_buckets_20260928/sergey_ling_level3_matched.jsonsergey_ling_level3_matched0——0.00%—added
testcases/emulator_verified/skill_buckets_20260928/sergey_ling_matched.jsonsergey_ling_matched0——0.00%—added
testcases/emulator_verified/skill_buckets_20260928/sergey_lumak_matched.jsonsergey_lumak_matched0——0.00%—added
testcases/emulator_verified/wip_renee_gwen_no_blastmaster_20260925/wip_gwen_only_1000l_300m_vs_3000l.jsonwip_gwen_only_1000l_300m_vs_3000l0——0.00%—added
testcases/emulator_verified/wip_renee_gwen_no_blastmaster_20260925/wip_renee_gwen_1000l_300m_vs_3000l.jsonwip_renee_gwen_1000l_300m_vs_3000l0——0.00%—added
testcases/emulator_verified/wip_renee_gwen_no_blastmaster_20260925/wip_renee_gwen_1000l_300m_vs_4500l.jsonwip_renee_gwen_1000l_300m_vs_4500l0——0.00%—added
testcases/emulator_verified/wip_renee_gwen_no_blastmaster_20260925/wip_renee_only_1000l_300m_vs_3000l.jsonwip_renee_only_1000l_300m_vs_3000l0——0.00%—added
testcases/heroes_unittests/Flint_tc.jsonflint_tc_10—-0.01%——retired
testcases/heroes_unittests/Flint_tc.jsonflint_tc_21—0.41%——retired

Code / Config Changes

Commits (2b3f7928 → fde24e4d)

fde24e4dFIx testcase runner roundingpiddlyminx29/09/2026, 15:44:09

Code Changes (Run A → Run B)

Index: simulator/config/hero_definitions/Bradley.json
===================================================================
--- simulator/config/hero_definitions/Bradley.json prev run
+++ simulator/config/hero_definitions/Bradley.json this run
@@ -23,9 +23,11 @@
},
"PowerShot": {
"description": "Bradley uses his expertise in suppressive artillery against the enemy vanguard, increasing Damage Dealt to Lancers by X% and to Infantry by Y% for all troops.",
"trigger": {
- "type": "battle_start"
+ "type": "engagement",
+ "source": "any",
+ "target": ["lancer", "infantry"]
},
"effects": {
"PowerShot/1": {
"type": "type.single_target.damage.up",
@@ -35,12 +37,14 @@
18,
24,
30
],
+ "engaged_with": ["lancer"],
"units": {
- "applies_vs": [
- "lancer"
- ]
+ "applies_to": "trigger.source"
+ },
+ "duration": {
+ "turns": { "count": 1 }
}
},
"PowerShot/2": {
"type": "type.single_target.damage.up",
@@ -50,12 +54,14 @@
15,
20,
25
],
+ "engaged_with": ["infantry"],
"units": {
- "applies_vs": [
- "infantry"
- ]
+ "applies_to": "trigger.source"
+ },
+ "duration": {
+ "turns": { "count": 1 }
}
}
}
},
Index: simulator/config/hero_definitions/Gordon.json
===================================================================
--- simulator/config/hero_definitions/Gordon.json prev run
+++ simulator/config/hero_definitions/Gordon.json this run
@@ -5,30 +5,41 @@
"skills": {
"VenomInfusion": {
"description": "Every 2 attacks, Lancers deal X% extra damage and apply poison to the target for 1 turn. Poisoned enemies deal Y% less damage.",
"trigger": {
- "type": "attack",
+ "type": "attack",
"every": 2,
"source": "lancer"
},
"effects": {
- "VenomInfusion/1": {
- "type": "type.normal.damage.up",
- "applies_to_damage_kinds": ["normal"],
- "value": [
- 20,
- 40,
- 60,
- 80,
- 100
- ],
+ "VenomInfusion/mark": {
"units": {
- "applies_to": "trigger.source",
- "applies_vs": "trigger.target"
+ "applies_to": "self.lancer",
+ "applies_vs": "enemy.any"
},
"duration": {
- "attacks": {
- "count": 1
+ "turns": { "count": 1 },
+ "attacks": { "count": 1 }
+ },
+ "trigger_effects": {
+ "VenomInfusion/1": {
+ "type": "extra_skill_attack",
+ "value": [20, 40, 60, 80, 100],
+ "units": {
+ "applies_to": "self.any",
+ "applies_vs": "parent.use.target"
+ },
+ "trigger_damage_jobs": [
+ {
+ "source": "use.source",
+ "target": "use.target",
+ "damage_kind": "normal"
+ }
+ ],
+ "duration": {
+ "turns": { "delay": 1, "count": 1 },
+ "attacks": { "count": 1 }
+ }
}
}
},
"VenomInfusion/2": {
@@ -60,9 +71,8 @@
},
"effects": {
"ChemicalTerror/1": {
"type": "active.hero.damage.up",
- "applies_to_damage_kinds": ["normal"],
"value": [
30,
60,
90,
@@ -75,10 +85,9 @@
]
},
"duration": {
"turns": {
- "count": 1,
- "delay": 1
+ "count": 1
}
},
"same_effect_stacking": "max"
},
@@ -100,30 +109,8 @@
"count": 1
}
},
"same_effect_stacking": "max"
- },
- "ChemicalTerror/3": {
- "type": "active.hero.damage.up",
- "applies_to_damage_kinds": ["skill"],
- "value": [
- 30,
- 60,
- 90,
- 120,
- 150
- ],
- "units": {
- "applies_to": [
- "lancer"
- ]
- },
- "duration": {
- "turns": {
- "count": 1
- }
- },
- "same_effect_stacking": "max"
}
}
},
"ToxicRelease": {
Index: simulator/config/hero_definitions/README.md
===================================================================
--- simulator/config/hero_definitions/README.md prev run
+++ simulator/config/hero_definitions/README.md this run
@@ -134,11 +134,12 @@
| --- | --- |
| `pre_battle` | Activated once while preparing the battle, before any runtime exists. Chance-free by definition and restricted to static `passive.*` effects; these feed the static damage profile (in-game: widget passives baked into player bonus stats). |
| `battle_start` | Is attempted once during battle setup, before round 1. `first`, `every`, `source`, and `target` do not gate this trigger type. |
| `turn` | Is attempted once at the start of matching simulator rounds. Turn triggers must not define `source` or `target`; scope their effects instead. |
+| `engagement` | Is attempted at turn start, after `turn` skills and before turn-created carriers are used, once for each living `source` unit whose chosen target this turn (including attack-order effects such as Ambusher) matches `target`. Effects may use `trigger.source`/`trigger.target`. Troop class advantages use it: a line engaged with its favored class is empowered for everything it deals that turn, including splash on other units. Bradley's Power Shot also uses it; a direct effect may add `engaged_with` (see Effects) to activate only for particular engaged classes. |
| `attack` | Is attempted procedurally for a matching **normal** attack, after locked-target exhaustion and pre-existing `no_attack` checks. Generated skill jobs do not evaluate it. |
-Only these three exact strings are scheduled by the runtime. An unknown string is not rejected by a closed trigger schema; it simply leaves the skill unscheduled.
+Only these exact strings are scheduled by the runtime. An unknown string is not rejected by a closed trigger schema; it simply leaves the skill unscheduled.
Each attack is one procedural cluster. Effects produced by an earlier attack can affect later attacks, but a later intent cannot retroactively change an earlier attack. Target order is applied while locking that attack's target, before its trigger stage.
### `trigger.probability`
@@ -194,10 +195,12 @@
For an `attack` trigger, `source` and `target` are matched directly against each intended normal attack. Reina demonstrates the reverse-side form: `source: "enemy.any"` and `target: "self.any"` listens for an enemy attack against Reina's side.
`turn` triggers reject `source` and `target`. They have no attack-shaped source or target; use concrete effect scopes. A broad turn-scoped effect can participate in every matching normal attack during its active window. For example, an `extra_skill_attack` applying to `self.any` with `turns.count: 1` can emit one extra job for each eligible troop attack that turn without matching the generated skill jobs.
-Ahmose's Viper Formation creates a one-turn Infantry `no_attack` effect on each scheduled turn, even without Infantry. Only using that effect to pause an attack creates its two `trigger_effects` for damage reduction. Those children start next turn and last two complete turns; an unused pause expires without granting protection. The configuration's `reason` records why activation and actual use are separate.
+Turn-triggered extra attacks run during self-applied `no_attack` pauses, but enemy-applied `no_attack` controls suppress them. Both prevent attack triggers, attack-triggered follow-ups, and normal cadence advancement. Ahmose/Wayne captures verify the self-pause behavior; Sonya/Hendrik and Sonya/Wayne captures verify stun suppression. Other control/skill combinations remain predictions unless separately verified.
+Ahmose's Viper Formation counts four actual Infantry attacks, then schedules an Infantry pause for the following turn and damage reduction for that turn and the next. Without Infantry attacks it earns neither pause nor protection.
+
`pre_battle` and `battle_start` never have an attack-shaped intent. Its `source` and `target` fields are compiled but never used for matching; use effect scopes, not trigger selectors, to scope a battle-start effect.
## Effects
@@ -214,8 +217,9 @@
| `applies_to_damage_kinds` | Non-empty array containing `normal` and/or `skill` | Optional runtime-modifier applicability gate. Omitted means both kinds. Restricts eligible damage jobs without changing the bucket selected by `type`. |
| `units` | `{ applies_to?, applies_vs? }` | Resolves which troop lines may receive/use the effect and which opposing troop lines it applies against. |
| `duration` | `{ turns?, attacks? }` | Optional round window and/or use limit. Omitted means permanent, except that extra-attack effects default to one turn and one attack. |
| `same_effect_stacking` | `add` or `max` | Controls overlap between live activations of the same modifier definition and scope. Omitted means `add`. |
+| `engaged_with` | Non-empty array of `infantry`, `lancer`, `marksman` | `engagement` skills only, on direct effects: activate this effect only for a line whose target this turn is one of these units. Lets one skill give a different bonus per engaged class (Power Shot). |
| `requires_effect` | Effect ID string | Applies a runtime damage modifier only while the named effect is applicable to the same damage job. |
| `value_evolution` | Evolution object | Optionally changes the effect value as rounds or uses advance. |
| `trigger_damage_jobs` | Non-empty array of job definitions | Required for `extra_skill_attack`; describes the damage jobs it emits. |
| `trigger_effects` | Object keyed by child effect ID | Defines children materialized only from an actual parent use. |
@@ -313,9 +317,9 @@
`applies_vs: "all"` is deliberately rejected; use `"any"`. `units.side` is also rejected. Side belongs in relation-qualified selectors such as `enemy.any`.
`trigger.source`/`trigger` resolve to an attack trigger's dealer side and unit. `trigger.target`/`target` resolve to its locked taker side and unit. Direct turn effects and pre-battle/battle-start effects reject attack-relative selectors. A nested effect instead uses `parent.use.source` and `parent.use.target`; these resolve from the concrete job or control in which the typed parent actually participated.
-An effect can define both its normal `type` and keyed `trigger_effects`. Its children activate only when the parent is actually used. A type-less effect is a normal-attack carrier: when its unit scopes match an eligible normal attack, it materializes its keyed children and consumes a use. The carrier is useful for bounded one-use consequences without inventing a combat bucket. Children are snapshotted after the parent's use, so a child cannot recursively affect the same consuming damage job.
+An effect can define both its normal `type` and keyed `trigger_effects`. Its children activate only when the parent is actually used. A type-less effect is a normal-attack carrier: when its unit scopes match an eligible normal attack, it materializes its keyed children and consumes a use. A carrier created by a `turn` trigger is instead used at the start of that turn, before any attack, by the first living unit in its `applies_to` scope against that unit's current target. A unit under an enemy `no_attack` control cannot use it, and an unused turn carrier is discarded. The carrier is useful for bounded one-use consequences without inventing a combat bucket. Children are snapshotted after the parent's use, so a child cannot recursively affect the same consuming damage job.
The parser also accepts `"friendly"`, but only as “all units on this field's default side.” It is not an owner-relative synonym in every context. No current hero definition uses it; use `self.any`, `enemy.any`, or `any` instead.
## Duration
@@ -347,9 +351,9 @@
For example, an attack-triggered effect created during round 3 with `{ "turns": { "delay": 1, "count": 2 } }` is active in rounds 4 and 5 and expires at the start of round 6.
An attack-triggered effect with `{ "turns": { "count": 1 } }` is available for later jobs in the same round, but expires before the next round begins. It does not mean “the next full turn.”
-The native troop skills MasterBrawler, Charge, and RangedStrike use source/target matchup conditions on their attack triggers, apply their buffs to `trigger.source`, and use `{ "turns": { "count": 1 } }`. They omit effect-level `applies_vs`: the matchup controls activation, not subsequent recipients. The buff applies to the triggering attack and later attacks by that troop type in the same round, including extra attacks against other troop types. Extra attacks do not activate these skills again.
+The native troop skills MasterBrawler, Charge, and RangedStrike, and Bradley's Power Shot, use `engagement` triggers with a source/target matchup, apply their buffs to `trigger.source`, and use `{ "turns": { "count": 1 } }`. They omit effect-level `applies_vs`: the matchup controls activation, not subsequent recipients. The buff applies to every hit that troop type deals that round, including extra attacks against other troop types.
**Battle-start delay gotcha.** Battle-start effects are created at setup round 0, while the earliest active round is clamped to 1. Consequently, both `turns.delay: 0` and `turns.delay: 1` start in round 1. A battle-start effect needs `delay: 2` to begin in round 2. No current hero definition combines `battle_start` with a turn delay, but the distinction matters when authoring one.
### `attacks`
@@ -458,14 +462,16 @@
Target-specific modifiers are selected against each generated job's actual recipient. They do not inherit the initiating normal attack's target or its matchup bonuses.
The extra-attack effect is charged once for the parent normal attack if at least one of its jobs runs. If its `value` is non-positive, every job lacks living source/target troops, or every job is skipped because its target is already exhausted, the effect is not charged and can remain available.
-Immediate and delayed hits share bucket evaluation, shield settlement, and target caps. Immediate hits calculate and consume their modifiers together. Delayed hits instead separate captured contributions from attack-limited target modifiers.
+Immediate and delayed hits are the same calculation. When an `extra_skill_attack` with a turn delay is materialized as a carrier's `trigger_effects` child, each of its jobs is calculated in full at that moment, with two exceptions. Shields are ignored: the kills land at the next turn start, before that turn's shields activate, so shields never absorb or get consumed by a delayed hit. Next-hit modifiers (attack-limited target-side modifiers such as Gwen's Eagle Vision) depend on what used the carrier. A turn-start carrier (Renee's Dream Mark) is its own damage event, so it takes and consumes the live ones. Extra damage attached to an attack (Gordon's Venom Infusion) is part of that attack: it gets exactly the next-hit modifiers the attack's normal hit applied and consumes nothing further. Only the resulting kills are deferred. They are applied at the start of the turn the child would activate, capped by that turn's troop snapshot and the round's per-target cap. Attacks in that turn are still declared against the snapshot, so a target the delayed hit wiped out is still attacked, for zero kills. The child itself never joins the live extra-attack index.
-When an `extra_skill_attack` is materialized as a carrier's `trigger_effects` child, its generated damage is retained on that child ActiveEffect. All modes retain owned dynamic factors and captured static terms so delivery can combine modifiers without recalculating the source's troop strength. Attack-limited target modifiers and shields are not selected, advanced, or consumed during capture. When a later normal attack matches the child's source and target scope, pending damage lands before that attack is declared and before it creates new effects. Delivery applies the currently eligible attack-limited target modifiers to the captured buckets, consumes their uses, and settles live shields and the current target-troop cap. If no matching attack occurs during the child's duration, the pending damage expires without consuming those modifiers or shields.
+Turn start runs in this order: delayed hits land; the previous turn's effects expire and this turn's scheduled effects activate, except shields; `turn` skills fire; `engagement` skills fire; turn-created carriers are used; scheduled shields activate.
Renee's Nightmare Trace uses this form with a one-turn delay: the even-round Lancer attack locks the target and calculates the damage, then a matching allied attack on the following odd round delivers it. Its child uses `self.any` for delivery eligibility; the retained job still uses the original Lancer source and locked target, allowing delivery after the Lancers die. The precise capture ordering and treatment of defensive state remain provisional reverse-engineering assumptions.
+Pending captured hits land before the matching normal attack's control check, even if that attack will be stunned or paused. New mark placement still requires an unblocked Lancer attack. Pure-Lancer Renee/Sonya captures verify both boundaries: a turn-6 stun prevents placement, while a mark placed on turn 10 still lands during the turn-11 stun. This does not make fresh scheduled strikes stun-immune.
+
Renee's Dreamslice mark increases both normal and skill damage taken by the marked target. Dreamcatcher's Lancer-specific increase remains restricted to normal damage.
All jobs emitted by one use read the parent extra-attack effect's same current `value`; its use/evolution is charged only after those jobs finish. Other attack-limited **modifier** effects are different: each generated skill job is a separate modifier use, and modifiers are charged after each job, so a one-job modifier can expire before the second target in the same extra attack is calculated.
@@ -486,13 +492,13 @@
- `applies_vs` is the would-be target;
- chance belongs on the skill's `trigger.probability` when needed; it decides whether the control effect is created;
- `value` is ignored.
-Attack-triggered controls are activated during the all-triggers phase and can cancel the same normal intent that triggered them. All matching attack skills have already attempted before the cancellation is applied.
+Pre-existing `no_attack` controls are checked before attack triggers. An attack-triggered control cannot cancel the normal attack that created it; it applies on a later eligible attack.
-If both a no-attack and dodge control apply to the same normal attack, no-attack wins. If several live controls of the same type match, the last one encountered is reported as the winning control; matching attack-limited controls can still be charged together. `same_effect_stacking` does not select among controls.
+If both a no-attack and dodge control apply to the same normal attack, no-attack wins. If several live controls of the same type match, the last one encountered is reported as the winning control; matching attack-limited controls can still be charged together. Any matching enemy-applied `no_attack` suppresses turn-triggered extras, even if a self-pause is the last reported control. `same_effect_stacking` does not select among controls.
-Controls are checked from the live index for each attack in procedural order. A one-use broad control expires after its first actual use and cannot be pre-attached to later attacks. `no_attack` emits no normal or extra job and advances no cadence counter. Dodge emits a zero-kill normal outcome with `dodged: true`, advances normal cadence, and allows attack triggers and extra jobs to continue.
+Controls are checked from the live index for each attack in procedural order. A one-use broad control expires after its first actual use and cannot be pre-attached to later attacks. `no_attack` emits no normal job and advances no cadence counter. Fresh turn-triggered extra jobs run only if every applicable `no_attack` is owned by the attacking side; attack-triggered follow-ups and new mark placement do not run. Previously captured hits land before the control check. Dodge emits a zero-kill normal outcome with `dodged: true`, advances normal cadence, and allows attack triggers and extra jobs to continue.
### `attack_order`
Overrides target preference for matching normal attacks. Although no current hero definition uses it, it is a supported native effect type.
@@ -519,11 +525,12 @@
| --- | --- |
| `hero_generation` | `SR`, `S1`, `S1_natalia`, `S1_jeronimo`, `S2` through `S10` |
| `troop_type` | `infantry`, `lancer`, `marksman` |
| `requirements[].type/value` | `engagement_type` with `rally` or `garrison`, always beginning at level 1 |
-| `trigger.type` | `pre_battle`, `battle_start`, `turn`, `attack` |
-| `trigger.source` | attack triggers only: omitted, `infantry`, `lancer`, `marksman`, `self.any`, `self.all`, `enemy.any` |
-| `trigger.target` | attack triggers only: omitted or `self.any` |
+| `trigger.type` | `pre_battle`, `battle_start`, `turn`, `engagement`, `attack` |
+| `trigger.source` | attack/engagement triggers only: omitted, `infantry`, `lancer`, `marksman`, `any`, `self.any`, `self.all`, `enemy.any` |
+| `trigger.target` | attack/engagement triggers only: omitted, `self.any`, or `["lancer", "infantry"]` |
+| `engaged_with` | only Bradley's Power Shot: `["lancer"]`, `["infantry"]` |
| `trigger.probability` | omitted or a five-level numeric percentage array |
| `trigger.first` | omitted, 4, or 5 |
| `trigger.every` | omitted, 2, 3, 4, 5, or 6 |
| `units.applies_to` | omitted; unit strings/arrays; `all`; relation-qualified selectors; attack-relative selectors; nested `parent.use.*` selectors |
Index: simulator/config/troop_skills.json
===================================================================
--- simulator/config/troop_skills.json prev run
+++ simulator/config/troop_skills.json this run
@@ -11,9 +11,9 @@
"value": 0
}
],
"trigger": {
- "type": "attack",
+ "type": "engagement",
"source": "infantry",
"target": "lancer"
},
"effects": {
@@ -162,9 +162,9 @@
"value": 0
}
],
"trigger": {
- "type": "attack",
+ "type": "engagement",
"source": "lancer",
"target": "marksman"
},
"effects": {
@@ -320,9 +320,9 @@
"value": 0
}
],
"trigger": {
- "type": "attack",
+ "type": "engagement",
"source": "marksman",
"target": "infantry"
},
"effects": {
Index: simulator/src/config.test.ts
===================================================================
--- simulator/src/config.test.ts prev run
+++ simulator/src/config.test.ts this run
@@ -320,8 +320,24 @@
assert.throws(() => loadSimulatorConfigFromDir(removedDelay), /unknown trigger_damage_jobs key delivery_delay_turns/i);
assert.throws(() => loadSimulatorConfigFromDir(removedExtraKind), /damage_kind.*normal.*skill/i);
});
+test("engaged_with is accepted only on engagement skill effects with valid units", () => {
+ const effect = (engagedWith: unknown) => ({
+ type: "type.single_target.damage.up",
+ value: 10,
+ engaged_with: engagedWith,
+ units: { applies_to: "trigger.source" },
+ duration: { turns: { count: 1 } }
+ });
+ const engagement = { type: "engagement", source: "any", target: ["lancer", "infantry"] };
+ assert.doesNotThrow(() => loadSimulatorConfigFromDir(writeConfigWithTroopEffect(effect(["lancer"]), engagement)));
+ assert.throws(() => loadSimulatorConfigFromDir(writeConfigWithTroopEffect(effect(["lancer"]))), /engaged_with.*engagement/i);
+ for (const invalid of [[], ["cavalry"], "lancer"]) {
+ assert.throws(() => loadSimulatorConfigFromDir(writeConfigWithTroopEffect(effect(invalid), engagement)), /engaged_with must be/i);
+ }
+});
+
test("loadSimulatorConfig accepts modifier damage-kind applicability and rejects invalid uses", () => {
const valid = writeConfigWithTroopEffect({
type: "active.hero.damageTaken.up",
applies_to_damage_kinds: ["normal"],
Index: simulator/src/config.ts
===================================================================
--- simulator/src/config.ts prev run
+++ simulator/src/config.ts this run
@@ -113,8 +113,9 @@
validateNativeEffectUnits(effect as EffectIntentDefinition, file, skillId, effectId);
validateNativeEffectValue(effect as EffectIntentDefinition, file, skillId, effectId);
validateAppliesToDamageKinds(effect as EffectIntentDefinition, file, skillId, effectId);
validateRequiredEffect(effect as EffectIntentDefinition, effectDefinitions, file, skillId, effectId);
+ validateEngagedWith(skill.trigger, effect as EffectIntentDefinition, nested, file, skillId, effectId);
validateEffectValueFormula(skill.trigger, effect as EffectIntentDefinition, file, skillId, effectId);
if (type === "attack_order") validateAttackOrderEffect(effect as EffectIntentDefinition, file, skillId, effectId);
validateNativeEffectDuration(effect as EffectIntentDefinition, file, skillId, effectId);
validateStaticBucketEffect(skill.trigger, effect as EffectIntentDefinition, file, skillId, effectId);
@@ -190,8 +191,27 @@
if (effect.trigger_effects) yield* walkEffectDefinitionsWithDepth(effect.trigger_effects, true);
}
}
+function validateEngagedWith(
+ trigger: SkillFile["skills"][string]["trigger"],
+ effect: EffectIntentDefinition,
+ nested: boolean,
+ file: string,
+ skillId: string,
+ effectId: string
+): void {
+ if (effect.engaged_with === undefined) return;
+ const path = `${file}:${skillId}.${effectId}.engaged_with`;
+ if (trigger.type !== "engagement" || nested) {
+ throw new Error(`engaged_with is only supported on direct effects of engagement skills at ${path}`);
+ }
+ const units = effect.engaged_with as unknown;
+ if (!Array.isArray(units) || units.length === 0 || !units.every((unit) => unit === "infantry" || unit === "lancer" || unit === "marksman")) {
+ throw new Error(`engaged_with must be a non-empty list of infantry, lancer or marksman at ${path}`);
+ }
+}
+
function validateRequiredEffect(
effect: EffectIntentDefinition,
effectDefinitions: Map<string, EffectIntentDefinition[]>,
file: string,
Index: simulator/src/damage.test.ts
===================================================================
--- simulator/src/damage.test.ts prev run
+++ simulator/src/damage.test.ts this run
@@ -3,9 +3,8 @@
import {
generateDamageJob,
deliverDamageJob,
- retainGeneratedDamage,
type DamageJobOptions,
ceilIgnoringFloatResidue,
createDamageScratch,
evaluateDamageExpression
@@ -106,11 +105,8 @@
if (!group) {
group = {
ordinal: groups.length,
bucketIndex: DYNAMIC_BUCKETS.findIndex((definition) => definition.name === activeEffect.intent.type),
- attackLimitedTakerModifier: activeEffect.kind === "modifier" &&
- activeEffect.duration.attacks !== undefined &&
- DYNAMIC_BUCKETS.find(definition => definition.name === activeEffect.intent.type)?.jobSide === "taker",
sameEffectStacking: activeEffect.sameEffectStacking
};
byResolvedGroup.set(key, group);
groups.push(group);
@@ -148,10 +144,11 @@
test("float-safe ceilings ignore residue but preserve genuine fractional troops", () => {
assert.equal(ceilIgnoringFloatResidue(200.00000000000003), 200);
assert.equal(ceilIgnoringFloatResidue(Math.sqrt(2) * Math.sqrt(2)), 2);
- assert.equal(ceilIgnoringFloatResidue(10 + 5e-13), 10);
- assert.equal(ceilIgnoringFloatResidue(10 + 2e-12), 11);
+ assert.equal(ceilIgnoringFloatResidue(10 + 5e-11), 10);
+ assert.equal(ceilIgnoringFloatResidue(10 + 2e-10), 11);
+ assert.equal(ceilIgnoringFloatResidue(200000 - 99999.99999999999), 100000);
assert.equal(ceilIgnoringFloatResidue(200.001), 201);
assert.equal(ceilIgnoringFloatResidue(0.01), 1);
});
@@ -490,91 +487,8 @@
assert.ok(Math.abs(damageTakenUp.kills - baseline.kills * 1.25) < 1e-12);
assert.ok(Math.abs(damageTakenDown.kills - baseline.kills / 1.25) < 1e-12);
});
-test("retained damage preserves generation modifiers and health conversion across scratch reuse", () => {
- for (const mode of ["fast", "trace"] as const) {
- const fighters = simpleFighters();
- const health = effect("active.hero.health.up", "defender", 100);
- const shield: ActiveEffect = {
- ...effect("active.hero.shield", "defender", 8),
- kind: "shield",
- duration: { turns: { count: 2 } },
- intent: {
- id: "source-shield",
- type: "active.hero.shield",
- value: 8,
- value_formula: { type: "percent_of", source: "trigger.source_attack" }
- }
- };
- const index = preparedEffectIndex([health, shield]);
- indexEffect(index, health);
- const options = damageJobOptions(fighters, [], {
- effectIndex: index,
- scratch: createDamageScratch(),
- recorder: createRecorder(mode, [], () => { throw new Error("damage-only recorder"); })
- });
- const expected = calculateIndexedDamageJob(job, fighters, [effect("active.hero.health.up", "defender", 100)]);
- const retained = retainGeneratedDamage(generateDamageJob(job, fighters, options));
- expireEffectIndex(index, health);
- const interveningJob = { ...job, sourceMultiplier: 3 };
- const intervening = deliverDamageJob(interveningJob, generateDamageJob(interveningJob, fighters, options), options);
- assert.ok(intervening.kills > expected.kills);
- indexEffect(index, shield);
- const delivered = deliverDamageJob({ ...job, round: 2 }, retained, options);
- assert.ok(Math.abs(delivered.kills - Math.max(0, expected.kills - 4)) < 1e-12, mode);
- if (mode === "trace") {
- assert.equal(delivered.trace?.atomicBuckets["active.hero.health.up"].totalPct, 100);
- assert.equal(delivered.trace?.damageBeforeOffsets, expected.kills);
- assert.equal(delivered.trace?.offsetDamage, 4);
- }
- }
-});
-
-test("damage generation leaves shield delay and protection untouched until delivery", () => {
- const fighters = simpleFighters();
- const shield: ActiveEffect = {
- ...effect("active.hero.shield", "defender", 4),
- kind: "shield",
- duration: { turns: { count: 2 } },
- remainingAttackDelay: 1
- };
- const options = damageJobOptions(fighters, [shield], { trace: true, scratch: createDamageScratch() });
- const baseline = calculateIndexedDamageJob(job, fighters, []).kills;
- const first = retainGeneratedDamage(generateDamageJob(job, fighters, options));
- const second = retainGeneratedDamage(generateDamageJob(job, fighters, options));
- const firstArrival = deliverDamageJob(job, first, options);
- const secondArrival = deliverDamageJob(job, second, options);
- assert.equal(firstArrival.kills, baseline);
- assert.equal(secondArrival.kills, Math.max(0, baseline - 4));
- assert.equal(firstArrival.trace?.offsetDamage, 0);
- assert.equal(secondArrival.trace?.offsetDamage, 4);
-});
-
-test("delayed damage ignores shields that expire or deplete before arrival", () => {
- const fighters = simpleFighters();
- const expired: ActiveEffect = {
- ...effect("active.hero.shield", "defender", 100),
- kind: "shield",
- duration: { turns: { count: 1 } }
- };
- const depleted: ActiveEffect = {
- ...effect("active.hero.shield", "defender", 3),
- kind: "shield",
- duration: { turns: { count: 2 } }
- };
- const options = damageJobOptions(fighters, [expired, depleted], { trace: true, scratch: createDamageScratch() });
- const baseline = calculateIndexedDamageJob(job, fighters, []).kills;
- const retained = retainGeneratedDamage(generateDamageJob(job, fighters, options));
- expireEffectIndex(options.effectIndex, expired);
- const preceding = deliverDamageJob(job, generateDamageJob(job, fighters, options), options);
- assert.equal(preceding.kills, Math.max(0, baseline - 3));
- const arrival = deliverDamageJob({ ...job, round: 2 }, retained, options);
- assert.equal(arrival.kills, baseline);
- assert.equal(arrival.trace?.offsetDamage, 0);
- assert.equal(arrival.appliedEffects?.some((applied) => "kind" in applied && applied.kind === "shield"), false);
-});
-
test("additive turn shields conserve same-group and independent budgets across successive hits", () => {
const fighters = simpleFighters();
const baseline = calculateIndexedDamageJob(job, fighters, []).kills;
const first: ActiveEffect = {
Index: simulator/src/damage.ts
===================================================================
--- simulator/src/damage.ts prev run
+++ simulator/src/damage.ts this run
@@ -89,9 +89,9 @@
const DYNAMIC_NEUTRAL_VALUES = Float64Array.from(DYNAMIC_BUCKETS.map(({ update }) => bucketNeutralValue(update)));
const STATIC_NEUTRAL_VALUES = Float64Array.from(STATIC_BUCKETS.map(({ update }) => bucketNeutralValue(update)));
const DAMAGE_SCALE_BUCKET = createEvaluatedDamageBucket(1 / 100);
// Smallest tested power-of-ten tolerance that removes the verified residue.
-const CEILING_EPSILON = 1e-12;
+const CEILING_EPSILON = 1e-10;
/** Smaller side's initial army; battle-invariant. */
export function minInitialArmy(fighters: Record<SideId, ResolvedFighter>): number {
return Math.max(
@@ -112,19 +112,31 @@
usedEffects?: ActiveEffect[];
/** Effects whose primary mechanic actually won/applied; excludes duration-only siblings. */
primaryUsedEffects?: ActiveEffect[];
minInitialArmy?: number;
+ /**
+ * Next-hit modifiers this job takes over from the attack it is part of, in place of the live
+ * next-hit groups: it neither reads nor consumes those (an attack-attached delayed hit).
+ */
+ inheritedNextHit?: readonly NextHitModifier[];
+ /** Shields neither absorb nor are consumed by this job (delayed hits land before shields go up). */
+ ignoreShields?: boolean;
}
-/** Immediate generation borrows scratch; captured generation owns its factors and static terms. */
+/** One next-hit modifier value as a damage job applied it. */
+export interface NextHitModifier {
+ effect: ActiveEffect;
+ bucketIndex: number;
+ value: number;
+}
+
+/** Borrowed from scratch: deliver it before generating the next job. */
export interface GeneratedDamage {
factors?: Float64Array;
recording: DamageJobRecorder;
armyTerm: number;
damageBeforeOffsets: number;
healthFactor: number;
- capturedDealerFactor?: number;
- capturedTakerFactor?: number;
}
export function generateDamageJob(
job: DamageJob,
@@ -150,90 +162,24 @@
buckets.factors[DYNAMIC_BUCKET_INDEX["active.troop.health.down"]];
return generated;
}
-export function retainGeneratedDamage(generated: GeneratedDamage): GeneratedDamage {
- return {
- factors: generated.recording.needsFactors ? generated.factors!.slice() : undefined,
- recording: generated.recording,
- armyTerm: generated.armyTerm,
- damageBeforeOffsets: generated.damageBeforeOffsets,
- healthFactor: generated.healthFactor
- };
-}
-
-/** Snapshot source/static/turn-based contributions; attack-limited taker modifiers wait for landing. */
-export function generateCapturedDamageJob(
- job: DamageJob,
- fighters: Record<SideId, ResolvedFighter>,
- options: DamageJobOptions
-): GeneratedDamage {
- const recording = options.recorder.startDamageJob();
- const buckets = options.scratch ? resetDamageScratch(options.scratch) : createNumericDamageBuckets();
- const usedEffects = options.usedEffects ?? [];
- const armyTerm = populateSourceBuckets(job, fighters, options, buckets);
- applyPartitionedBucketEffects(job, options.effectIndex, buckets, recording, usedEffects, options.primaryUsedEffects ?? usedEffects, false);
- const capturedDealerFactor = bucketSetValue(options.staticDamageProfile[job.dealerSide][job.dealerUnit].dealerFactor);
- const capturedTakerFactor = bucketSetValue(options.staticDamageProfile[job.takerSide][job.takerUnit].takerFactor);
- const expression = DYNAMIC_EXPRESSIONS[job.kind];
- const damageBeforeOffsets = multiplySlots(1, buckets.factors, expression.numeratorSlots) /
- multiplySlots(1, buckets.factors, expression.denominatorSlots) *
- capturedDealerFactor * capturedTakerFactor * bucketSetValue(DAMAGE_SCALE_BUCKET);
- return {
- factors: options.scratch ? buckets.factors.slice() : buckets.factors,
- recording,
- armyTerm,
- damageBeforeOffsets,
- healthFactor: dynamicHealthFactor(buckets.factors),
- capturedDealerFactor,
- capturedTakerFactor
- };
-}
-
-/** Add current taker attack budgets to the original buckets, then settle current shields and caps. */
-export function deliverCapturedDamageJob(
- job: DamageJob,
- generated: GeneratedDamage,
- options: DamageJobOptions
-): DamageResult {
- const buckets = options.scratch ? resetDamageScratch(options.scratch) : createNumericDamageBuckets();
- buckets.factors.set(generated.factors!);
- const usedEffects = options.usedEffects ?? [];
- applyPartitionedBucketEffects(job, options.effectIndex, buckets, generated.recording, usedEffects, options.primaryUsedEffects ?? usedEffects, true);
- const expression = DYNAMIC_EXPRESSIONS[job.kind];
- // Re-evaluate, rather than dividing by captured factors: a captured bucket may be zero.
- const damageBeforeOffsets = multiplySlots(1, buckets.factors, expression.numeratorSlots) /
- multiplySlots(1, buckets.factors, expression.denominatorSlots) *
- generated.capturedDealerFactor! * generated.capturedTakerFactor! * bucketSetValue(DAMAGE_SCALE_BUCKET);
- return finishDamageJob(job, generated, options, buckets, damageBeforeOffsets, dynamicHealthFactor(buckets.factors));
-}
-
export function deliverDamageJob(
job: DamageJob,
generated: GeneratedDamage,
options: DamageJobOptions
): DamageResult {
- const { damageBeforeOffsets, healthFactor } = generated;
+ const { recording, armyTerm, damageBeforeOffsets, healthFactor } = generated;
const buckets = options.scratch ?? createNumericDamageBuckets();
if (generated.factors !== buckets.factors) {
resetDamageScratch(buckets);
if (generated.factors) buckets.factors.set(generated.factors);
}
- return finishDamageJob(job, generated, options, buckets, damageBeforeOffsets, healthFactor);
-}
-
-function finishDamageJob(
- job: DamageJob,
- generated: GeneratedDamage,
- options: DamageJobOptions,
- buckets: NumericDamageBuckets,
- damageBeforeOffsets: number,
- healthFactor: number
-): DamageResult {
- const { recording, armyTerm } = generated;
const usedEffects = options.usedEffects ?? [];
const primaryUsedEffects = options.primaryUsedEffects ?? usedEffects;
- const offsetDamage = applyShields(job, options.effectIndex, buckets, recording, damageBeforeOffsets, healthFactor, usedEffects, primaryUsedEffects);
+ const offsetDamage = options.ignoreShields
+ ? 0
+ : applyShields(job, options.effectIndex, buckets, recording, damageBeforeOffsets, healthFactor, usedEffects, primaryUsedEffects);
const rawDamage = Math.max(0, damageBeforeOffsets - offsetDamage);
const takerTroops = job.roundStartTroops[job.takerSide][job.takerUnit] ?? 0;
const kills = options.capToTakerTroops === false ? rawDamage : Math.min(takerTroops, rawDamage);
return recording.finish({ job, factors: buckets.factors, armyTerm, damageBeforeOffsets, offsetDamage, rawDamage, kills });
@@ -247,63 +193,59 @@
recording: DamageJobRecorder,
usedEffects: ActiveEffect[],
primaryUsedEffects: ActiveEffect[]
): number {
- const armyTerm = populateSourceBuckets(job, fighters, options, buckets);
+ // Fractional casualties carry between rounds, but every positive remainder is
+ // still one living troop and contributes fully to the next attack.
+ const dealerTroops = ceilIgnoringFloatResidue(
+ Math.max(0, job.roundStartTroops[job.dealerSide][job.dealerUnit] ?? 0)
+ );
+ const initialArmy = options.minInitialArmy ?? minInitialArmy(fighters);
+ const armyTerm = Math.sqrt(dealerTroops) * Math.sqrt(initialArmy);
+ applyDynamicDamageBucketValue(buckets, "troops.count", armyTerm);
+ applyDynamicDamageBucketValue(buckets, "source.multiplier", job.sourceMultiplier ?? 1);
const jobSlot = damageJobSlot(job);
+ const groups = options.effectIndex.damageGroupsByJobShape[jobSlot];
+ const inherited = options.inheritedNextHit;
applyBucketEffects(
- options.effectIndex.damageGroupsByJobShape[jobSlot],
+ inherited ? groups.filter((group) => !group.nextHit) : groups,
options.effectIndex.liveEffectsByGroup,
job.round,
jobSlot,
buckets,
recording,
usedEffects,
primaryUsedEffects
);
+ if (inherited) {
+ for (const { effect, bucketIndex, value } of inherited) {
+ applyBucketValue(buckets.factors, bucketIndex, value, BUCKET_UPDATE_BY_INDEX);
+ recording.recordEffect(effect, value);
+ }
+ }
return armyTerm;
}
-function populateSourceBuckets(
- job: DamageJob,
- fighters: Record<SideId, ResolvedFighter>,
- options: DamageJobOptions,
- buckets: NumericDamageBuckets
-): number {
- // Fractional casualties carry between rounds, but every positive remainder is
- // still one living troop and contributes fully to the next attack.
- const dealerTroops = ceilIgnoringFloatResidue(
- Math.max(0, job.roundStartTroops[job.dealerSide][job.dealerUnit] ?? 0)
- );
- const initialArmy = options.minInitialArmy ?? minInitialArmy(fighters);
- const armyTerm = Math.sqrt(dealerTroops) * Math.sqrt(initialArmy);
- applyDynamicDamageBucketValue(buckets, "troops.count", armyTerm);
- applyDynamicDamageBucketValue(buckets, "source.multiplier", job.sourceMultiplier ?? 1);
- return armyTerm;
-}
-
-function dynamicHealthFactor(factors: Float64Array): number {
- return factors[DYNAMIC_BUCKET_INDEX["active.hero.health.up"]] *
- factors[DYNAMIC_BUCKET_INDEX["active.troop.health.up"]] /
- factors[DYNAMIC_BUCKET_INDEX["active.hero.health.down"]] /
- factors[DYNAMIC_BUCKET_INDEX["active.troop.health.down"]];
-}
-
-function applyPartitionedBucketEffects(
- job: DamageJob,
- index: EffectIndex,
- buckets: NumericDamageBuckets,
- recording: DamageJobRecorder,
- usedEffects: ActiveEffect[],
- primaryUsedEffects: ActiveEffect[],
- attackLimitedTakerModifier: boolean
-): void {
- const jobSlot = damageJobSlot(job);
- for (const group of index.damageGroupsByJobShape[jobSlot]) {
- if (group.attackLimitedTakerModifier !== attackLimitedTakerModifier) continue;
- applyEffectGroup(group, index.liveEffectsByGroup, job.round, jobSlot, buckets, recording, usedEffects, primaryUsedEffects, attackLimitedTakerModifier);
+/**
+ * The next-hit modifier values a job applied, reconstructed from the effects it used: a
+ * max-stacking group contributes its largest value, an additive group every value.
+ */
+export function appliedNextHitModifiers(usedEffects: readonly ActiveEffect[], round: number): NextHitModifier[] {
+ const applied: NextHitModifier[] = [];
+ const maxByGroup = new Map<ActiveEffectGroup, NextHitModifier>();
+ for (const effect of usedEffects) {
+ const group = effect.effectGroup;
+ if (!group?.nextHit) continue;
+ const modifier = { effect, bucketIndex: group.bucketIndex, value: effect.getCurrentValue(round) };
+ if (group.sameEffectStacking !== "max") {
+ applied.push(modifier);
+ continue;
+ }
+ const previous = maxByGroup.get(group);
+ if (!previous || modifier.value > previous.value) maxByGroup.set(group, modifier);
}
+ return [...applied, ...maxByGroup.values()];
}
function applyBucketEffects(
@@ -328,13 +270,12 @@
jobSlot: number,
buckets: NumericDamageBuckets,
recording: DamageJobRecorder,
usedEffects: ActiveEffect[],
- primaryUsedEffects: ActiveEffect[],
- attackLimitedTakerModifier?: boolean
+ primaryUsedEffects: ActiveEffect[]
): void {
const dependency = group.requiredGroupOrdinalsByJobShape;
- if (dependency && !requiredEffectIsApplicable(dependency, liveEffectsByGroup, round, jobSlot, attackLimitedTakerModifier)) return;
+ if (dependency && !requiredEffectIsApplicable(dependency, liveEffectsByGroup, round, jobSlot)) return;
const effects = liveEffectsByGroup[group.ordinal];
if (effects.length === 0) return;
if (group.sameEffectStacking !== "max") {
for (const effect of effects) {
@@ -416,17 +357,14 @@
function requiredEffectIsApplicable(
dependency: Array<number[] | undefined>,
liveEffectsByGroup: ActiveEffect[][],
round: number,
- jobSlot: number,
- attackLimitedTakerModifier?: boolean
+ jobSlot: number
): boolean {
const requiredOrdinals = dependency[jobSlot];
if (!requiredOrdinals) return false;
for (const ordinal of requiredOrdinals) {
for (const effect of liveEffectsByGroup[ordinal]) {
- if (attackLimitedTakerModifier !== undefined &&
- effect.effectGroup!.attackLimitedTakerModifier !== attackLimitedTakerModifier) continue;
if (isEffectAttackReady(effect) && effect.getCurrentValue(round) !== 0) return true;
}
}
return false;
Index: simulator/src/effectIndex.test.ts
===================================================================
--- simulator/src/effectIndex.test.ts prev run
+++ simulator/src/effectIndex.test.ts this run
@@ -121,9 +121,8 @@
if (!group) {
group = {
ordinal: groups.length,
bucketIndex: 0,
- attackLimitedTakerModifier: false,
sameEffectStacking: effect.sameEffectStacking
};
byResolvedGroup.set(key, group);
groups.push(group);
Index: simulator/src/extraAttacks.ts
===================================================================
--- simulator/src/extraAttacks.ts prev run
+++ simulator/src/extraAttacks.ts this run
@@ -11,34 +11,34 @@
import { advanceEffectAttackDelay } from "./effects";
import {
generateDamageJob,
deliverDamageJob,
- generateCapturedDamageJob,
- deliverCapturedDamageJob,
ceilIgnoringFloatResidue,
- type DamageJobOptions
+ type DamageJobOptions,
+ type NextHitModifier
} from "./damage";
import { normalizeUnitType } from "./normalize";
+import type { BattleRecorder } from "./recorder";
import {
capJobToRemainingTarget,
chargeEffectUse,
chargeUsedEffectsForJob,
effectUseIntent,
+ expireActiveEffect,
materializeTriggeredEffects,
targetExhausted,
type DamageJobResult,
type RunLoopOptions,
type Runtime
} from "./runtime";
-// Pending jobs land before the matching normal attack is declared; immediate jobs
-// run after its damage. Each effect participates in only one phase, so its attack
-// delay advances once per matching normal attack. Only immediate skill kills feed
-// that normal attack's deferred formulas.
-// The effect is charged one use only when at least one of its own jobs actually ran.
-// Snapshot eligible effects before delivery because charging may expire them.
+// Immediate jobs run after the normal attack's damage. A self-paused normal attack still
+// permits turn-triggered jobs, but not attack-triggered follow-ups. Only immediate skill
+// kills feed deferred formulas. The effect is charged one use only when at least one of
+// its own jobs actually ran. Snapshot eligible effects before delivery because charging
+// may expire them.
export function processExtraAttacks(
- phase: "pending" | "immediate",
+ phase: "immediate" | "cancelled",
normalAttack: DamageJob,
intent: AttackIntent,
runtime: Runtime,
fighters: Record<SideId, ResolvedFighter>,
@@ -54,92 +54,53 @@
let skillKills = 0;
// Snapshot the applicable effects: charging an effect below may expire it out of the live index.
const effects = runtime.effectIndex.extraAttacks.filter(
(effect) =>
- (phase === "pending" ? (effect.pendingDamageJobs?.length ?? 0) > 0 : effect.pendingDamageJobs === undefined) &&
+ (phase !== "cancelled" || effect.sourceSkill?.trigger.type === "turn") &&
extraAttackEffectAppliesToNormalAttack(effect, normalAttack) &&
advanceEffectAttackDelay(effect)
);
for (const effect of effects) {
const sourceEffectId = effect.source.effectId ?? effect.intent.id;
let processedJobCount = 0;
let firstProcessedJob: DamageJob | undefined;
- if (effect.pendingDamageJobs) {
- const pendingJobs = effect.pendingDamageJobs;
- let retainedJobCount = 0;
- for (const pending of pendingJobs) {
- const deliveryJob: DamageJob = {
- ...pending.job,
- round,
- calculationRound: pending.job.round,
- roundStartTroops
- };
- if (loopOptions.capRoundKills && targetExhausted(deliveryJob, roundStartTroops, roundTargetDamage)) {
- pendingJobs[retainedJobCount++] = pending;
- continue;
- }
- recorder.recordScheduledDamageJob(deliveryJob);
- const result = deliverCapturedDamageJob(deliveryJob, pending.generated, damageJobOptions);
- if (deliveryJob.kind === "skill") recorder.recordSkillDamageJob(deliveryJob, effect);
- if (loopOptions.capRoundKills) {
- capJobToRemainingTarget(result, deliveryJob, roundStartTroops, roundTargetDamage, recorder);
- } else if (loopOptions.capJobKills) {
- result.kills = Math.min(
- result.kills,
- Math.max(0, roundStartTroops[deliveryJob.takerSide][deliveryJob.takerUnit] ?? 0)
- );
- recorder.recordFinalKills(result);
- }
- results.push({ job: deliveryJob, result, intent });
- totalKills += result.kills;
- if (deliveryJob.kind === "skill") {
- skillKills += result.kills;
- runtime.extraSkillAttackJobsByEffect[sourceEffectId] = (runtime.extraSkillAttackJobsByEffect[sourceEffectId] ?? 0) + 1;
- }
- processedJobCount += 1;
- firstProcessedJob ??= deliveryJob;
- chargeUsedEffectsForJob(runtime, deliveryJob, recorder);
- }
- pendingJobs.length = retainedJobCount;
- } else {
- for (const definition of effect.triggerDamageJobs ?? []) {
- const sources = resolveTriggerJobSelector(definition.source, "source", effect, normalAttack, roundStartTroops);
- const targets = resolveTriggerJobSelector(definition.target, "target", effect, normalAttack, roundStartTroops);
- const multiplier = effect.getCurrentValue(round) / 100;
- if (multiplier <= 0) continue;
- for (const source of sources) {
- if (ceilIgnoringFloatResidue(roundStartTroops[source.side][source.unit] ?? 0) <= 0) continue;
- for (const target of targets) {
- if (ceilIgnoringFloatResidue(roundStartTroops[target.side][target.unit] ?? 0) <= 0) continue;
- const job: DamageJob = {
- round,
- kind: definition.damage_kind ?? "skill",
- roundStartTroops,
- dealerSide: source.side,
- dealerUnit: source.unit,
- takerSide: target.side,
- takerUnit: target.unit,
- sourceEffectId,
- sourceMultiplier: multiplier
- };
- if (loopOptions.capRoundKills && targetExhausted(job, roundStartTroops, roundTargetDamage)) continue;
- recorder.recordScheduledDamageJob(job);
- const generated = generateDamageJob(job, fighters, damageJobOptions);
- const result = deliverDamageJob(job, generated, damageJobOptions);
- if (job.kind === "skill") recorder.recordSkillDamageJob(job, effect);
- if (loopOptions.capRoundKills) {
- capJobToRemainingTarget(result, job, roundStartTroops, roundTargetDamage, recorder);
- }
- results.push({ job, result, intent });
- totalKills += result.kills;
- if (job.kind === "skill") {
- skillKills += result.kills;
- runtime.extraSkillAttackJobsByEffect[sourceEffectId] = (runtime.extraSkillAttackJobsByEffect[sourceEffectId] ?? 0) + 1;
- }
- processedJobCount += 1;
- firstProcessedJob ??= job;
- chargeUsedEffectsForJob(runtime, job, recorder);
+ for (const definition of effect.triggerDamageJobs ?? []) {
+ const sources = resolveTriggerJobSelector(definition.source, "source", effect, normalAttack, roundStartTroops);
+ const targets = resolveTriggerJobSelector(definition.target, "target", effect, normalAttack, roundStartTroops);
+ const multiplier = effect.getCurrentValue(round) / 100;
+ if (multiplier <= 0) continue;
+ for (const source of sources) {
+ if (ceilIgnoringFloatResidue(roundStartTroops[source.side][source.unit] ?? 0) <= 0) continue;
+ for (const target of targets) {
+ if (ceilIgnoringFloatResidue(roundStartTroops[target.side][target.unit] ?? 0) <= 0) continue;
+ const job: DamageJob = {
+ round,
+ kind: definition.damage_kind ?? "skill",
+ roundStartTroops,
+ dealerSide: source.side,
+ dealerUnit: source.unit,
+ takerSide: target.side,
+ takerUnit: target.unit,
+ sourceEffectId,
+ sourceMultiplier: multiplier
+ };
+ if (loopOptions.capRoundKills && targetExhausted(job, roundStartTroops, roundTargetDamage)) continue;
+ recorder.recordScheduledDamageJob(job);
+ const generated = generateDamageJob(job, fighters, damageJobOptions);
+ const result = deliverDamageJob(job, generated, damageJobOptions);
+ if (job.kind === "skill") recorder.recordSkillDamageJob(job, effect);
+ if (loopOptions.capRoundKills) {
+ capJobToRemainingTarget(result, job, roundStartTroops, roundTargetDamage, recorder);
}
+ results.push({ job, result, intent });
+ totalKills += result.kills;
+ if (job.kind === "skill") {
+ skillKills += result.kills;
+ runtime.extraSkillAttackJobsByEffect[sourceEffectId] = (runtime.extraSkillAttackJobsByEffect[sourceEffectId] ?? 0) + 1;
+ }
+ processedJobCount += 1;
+ firstProcessedJob ??= job;
+ chargeUsedEffectsForJob(runtime, job, recorder);
}
}
}
if (processedJobCount > 0) {
@@ -150,24 +111,38 @@
}
return { totalKills, skillKills };
}
-export function captureTriggeredExtraDamage(
+// A delayed extra attack (a carrier child with a turn delay) is calculated in full when its
+// parent is used; only its kills are deferred, landing at the start of the turn the child
+// would activate. Landing precedes that turn's shields, so shields never touch it.
+// Next-hit modifiers depend on what the parent use was:
+// - a turn-start event (Renee's Dream Mark) is its own damage event, so it takes and consumes
+// the live next-hit modifiers (inheritedNextHit omitted);
+// - extra damage attached to an attack (Gordon's Venom Infusion) is part of that attack: it
+// gets exactly the next-hit modifiers the attack's normal hit applied, consuming nothing more.
+export function calculateDelayedDamage(
effects: ActiveEffect[],
- normalAttack: DamageJob,
+ parentUse: DamageJob,
fighters: Record<SideId, ResolvedFighter>,
damageJobOptions: DamageJobOptions,
- runtime: Runtime
+ runtime: Runtime,
+ inheritedNextHit?: readonly NextHitModifier[]
): void {
- const { round, roundStartTroops } = normalAttack;
+ const { round, roundStartTroops } = parentUse;
+ const settleOptions: DamageJobOptions = {
+ ...damageJobOptions,
+ capToTakerTroops: false,
+ ignoreShields: true,
+ ...(inheritedNextHit ? { inheritedNextHit } : {})
+ };
for (const effect of effects) {
- if (effect.expired || effect.intent.type !== "extra_skill_attack" || effect.pendingDamageJobs) continue;
+ if (effect.expired || effect.intent.type !== "extra_skill_attack") continue;
+ const landingRound = Math.max(effect.startRound, round + 1);
const multiplier = effect.getCurrentValue(round) / 100;
- if (multiplier <= 0) continue;
- const pending = [];
- for (const definition of effect.triggerDamageJobs ?? []) {
- const sources = resolveTriggerJobSelector(definition.source, "source", effect, normalAttack, roundStartTroops);
- const targets = resolveTriggerJobSelector(definition.target, "target", effect, normalAttack, roundStartTroops);
+ for (const definition of multiplier > 0 ? effect.triggerDamageJobs ?? [] : []) {
+ const sources = resolveTriggerJobSelector(definition.source, "source", effect, parentUse, roundStartTroops);
+ const targets = resolveTriggerJobSelector(definition.target, "target", effect, parentUse, roundStartTroops);
for (const source of sources) {
if (ceilIgnoringFloatResidue(roundStartTroops[source.side][source.unit] ?? 0) <= 0) continue;
for (const target of targets) {
if (ceilIgnoringFloatResidue(roundStartTroops[target.side][target.unit] ?? 0) <= 0) continue;
@@ -181,18 +156,55 @@
takerUnit: target.unit,
sourceEffectId: effect.source.effectId ?? effect.intent.id,
sourceMultiplier: multiplier
};
- const generated = generateCapturedDamageJob(job, fighters, damageJobOptions);
- pending.push({ job, generated });
+ const result = deliverDamageJob(job, generateDamageJob(job, fighters, settleOptions), settleOptions);
chargeUsedEffectsForJob(runtime, job, damageJobOptions.recorder);
+ const landing = runtime.delayedDamageByRound[landingRound];
+ const pending = { job, result, effect };
+ if (landing) landing.push(pending);
+ else runtime.delayedDamageByRound[landingRound] = [pending];
}
}
}
- if (pending.length > 0) effect.pendingDamageJobs = pending;
+ // The child has done its work; it never joins the live extra-attack index.
+ expireActiveEffect(runtime, effect);
}
}
+/** Apply kills of delayed hits due this turn, capped by the turn's troop snapshot and round cap. */
+export function landDelayedDamage(
+ round: number,
+ runtime: Runtime,
+ roundStartTroops: DamageJob["roundStartTroops"],
+ roundTargetDamage: Record<SideId, Record<UnitType, number>>,
+ loopOptions: RunLoopOptions,
+ recorder: BattleRecorder,
+ results: DamageJobResult[]
+): void {
+ const landing = runtime.delayedDamageByRound[round];
+ if (!landing) return;
+ runtime.delayedDamageByRound[round] = undefined;
+ for (const { job, result: settled, effect } of landing) {
+ const deliveryJob: DamageJob = { ...job, round, calculationRound: job.round, roundStartTroops };
+ if (targetExhausted(deliveryJob, roundStartTroops, roundTargetDamage)) continue;
+ recorder.recordScheduledDamageJob(deliveryJob);
+ const result = { ...settled };
+ if (loopOptions.capRoundKills) {
+ capJobToRemainingTarget(result, deliveryJob, roundStartTroops, roundTargetDamage, recorder);
+ } else if (loopOptions.capJobKills) {
+ result.kills = Math.min(result.kills, Math.max(0, roundStartTroops[deliveryJob.takerSide][deliveryJob.takerUnit] ?? 0));
+ recorder.recordFinalKills(result);
+ }
+ if (deliveryJob.kind === "skill") {
+ recorder.recordSkillDamageJob(deliveryJob, effect);
+ const sourceEffectId = effect.source.effectId ?? effect.intent.id;
+ runtime.extraSkillAttackJobsByEffect[sourceEffectId] = (runtime.extraSkillAttackJobsByEffect[sourceEffectId] ?? 0) + 1;
+ }
+ results.push({ job: deliveryJob, result, intent: effectUseIntent(deliveryJob) });
+ }
+}
+
function extraAttackEffectAppliesToNormalAttack(effect: ActiveEffect, normalAttack: DamageJob): boolean {
return (
effect.appliesTo.side === normalAttack.dealerSide &&
unitMaskHas(effect.appliesTo.units, normalAttack.dealerUnit) &&
Index: simulator/src/runtime.ts
===================================================================
--- simulator/src/runtime.ts prev run
+++ simulator/src/runtime.ts this run
@@ -27,8 +27,10 @@
// Live troop counts, initialized from the fighters' immutable initialTroops.
troops: Record<SideId, Record<UnitType, number>>;
activateEffectsByRound: Array<ActiveEffect[] | undefined>;
expireEffectsByRound: Array<ActiveEffect[] | undefined>;
+ // Delayed hits already settled in full, keyed by the round whose start applies their kills.
+ delayedDamageByRound: Array<DelayedDamage[] | undefined>;
// Per-job scratch: effects that affected the job being calculated; drained by
// chargeUsedEffects (uses += 1 each) after every job in every mode.
usedEffects: ActiveEffect[];
primaryUsedEffects: ActiveEffect[];
@@ -54,8 +56,14 @@
takerSide: SideId;
};
}
+export interface DelayedDamage {
+ job: DamageJob;
+ result: DamageResult;
+ effect: ActiveEffect;
+}
+
export interface DamageJobResult {
job: DamageJob;
result: DamageResult;
intent?: AttackIntent;
@@ -72,8 +80,9 @@
skills.damageGroupsByJobShape
),
activateEffectsByRound: [],
expireEffectsByRound: [],
+ delayedDamageByRound: [],
usedEffects: [],
primaryUsedEffects: [],
staticDamageProfile,
damageScratch: createDamageScratch(),
@@ -112,23 +121,38 @@
if (effects) effects.push(effect);
else schedule[round] = [effect];
}
+// Shields scheduled for this turn are held back for activateScheduledShields, which runs
+// after turn-trigger skills: a turn-start event (Renee's Dream Mark) never meets a shield
+// scheduled by the previous turn's attacks.
export function processEffectSchedule(runtime: Runtime, round: number): void {
const expiring = runtime.expireEffectsByRound[round];
if (expiring) {
for (const effect of expiring) expireActiveEffect(runtime, effect);
runtime.expireEffectsByRound[round] = undefined;
}
const activating = runtime.activateEffectsByRound[round];
if (activating) {
+ const shields: ActiveEffect[] = [];
for (const effect of activating) {
- if (!effect.expired) indexEffect(runtime.effectIndex, effect);
+ if (effect.expired) continue;
+ if (effect.kind === "shield") shields.push(effect);
+ else indexEffect(runtime.effectIndex, effect);
}
- runtime.activateEffectsByRound[round] = undefined;
+ runtime.activateEffectsByRound[round] = shields.length > 0 ? shields : undefined;
}
}
+export function activateScheduledShields(runtime: Runtime, round: number): void {
+ const shields = runtime.activateEffectsByRound[round];
+ if (!shields) return;
+ for (const effect of shields) {
+ if (!effect.expired) indexEffect(runtime.effectIndex, effect);
+ }
+ runtime.activateEffectsByRound[round] = undefined;
+}
+
export function triggerSkills(
triggerType: "battle_start" | "round_start" | "attack_declared",
round: number,
skills: ResolvedSkill[],
@@ -235,12 +259,14 @@
export function targetExhausted(
job: DamageJob,
roundStartTroops: DamageJob["roundStartTroops"],
- roundTargetDamage: Record<SideId, Record<UnitType, number>>
+ roundTargetDamage: Record<SideId, Record<UnitType, number>>,
+ ignoredTargetDamage?: Record<SideId, Record<UnitType, number>>
): boolean {
const available = Math.max(0, roundStartTroops[job.takerSide][job.takerUnit] ?? 0);
- return ceilIgnoringFloatResidue(Math.max(0, available - roundTargetDamage[job.takerSide][job.takerUnit])) === 0;
+ const damaged = roundTargetDamage[job.takerSide][job.takerUnit] - (ignoredTargetDamage?.[job.takerSide][job.takerUnit] ?? 0);
+ return ceilIgnoringFloatResidue(Math.max(0, available - damaged)) === 0;
}
export function capJobToRemainingTarget(
result: DamageResult,
Index: simulator/src/runtimeSkills.ts
===================================================================
--- simulator/src/runtimeSkills.ts prev run
+++ simulator/src/runtimeSkills.ts this run
@@ -22,8 +22,9 @@
// Chance-free static-passive skills activated once at prepare time, before any runtime exists.
preBattle: ResolvedSkill[];
battleStart: ResolvedSkill[];
roundStart: PreparedRoundSkill[];
+ engagement: PreparedEngagementSkill[];
attackDeclaredByJobShape: Array<PreparedAttackSkill[] | undefined>;
effectGroups: ActiveEffectGroup[];
damageGroupsByJobShape: ActiveEffectGroup[][];
randomness: BattleRandomness;
@@ -42,8 +43,13 @@
everyAttack?: number;
firstAttack?: number;
}
+export interface PreparedEngagementSkill {
+ skill: ResolvedSkill;
+ probabilityPct: number;
+}
+
export interface PreparedRoundSkill {
skill: ResolvedSkill;
probabilityPct: number;
everyTurn?: number;
@@ -54,8 +60,9 @@
const all = fighters.flatMap((fighter) => [...(fighter.heroSkills ?? []), ...fighter.troopSkills]);
const preBattle: ResolvedSkill[] = [];
const battleStart: ResolvedSkill[] = [];
const roundStart: PreparedRoundSkill[] = [];
+ const engagement: PreparedEngagementSkill[] = [];
const attackDeclaredByJobShape: Array<PreparedAttackSkill[] | undefined> = Array.from({ length: DAMAGE_JOB_SHAPE_SLOTS });
const chanceSkillIds: Record<SideId, string[]> = { attacker: [], defender: [] };
const effectGroups: ActiveEffectGroup[] = [];
const damageGroupsByJobShape: ActiveEffectGroup[][] = Array.from({ length: DAMAGE_JOB_SHAPE_SLOTS }, () => []);
@@ -89,8 +96,10 @@
everyTurn: skill.trigger.every,
firstTurn: skill.trigger.first ?? skill.trigger.every
})
});
+ } else if (skill.trigger.type === "engagement") {
+ engagement.push({ skill, probabilityPct: trigger.probabilityPct });
} else if (skill.trigger.type === "attack") {
const immediateEffects: ResolvedEffectIntentDefinition[] = [];
const deferredIntents = skill.effects.filter((intent) => intent.value_formula !== undefined);
for (const intent of skill.effects) {
@@ -142,10 +151,9 @@
const group: ActiveEffectGroup = {
ordinal: effectGroups.length,
bucketIndex: damageBucketIndex(definition.name),
sameEffectStacking: effect.sameEffectStacking,
- attackLimitedTakerModifier: effect.kind === "modifier" &&
- effect.duration.attacks !== undefined && definition.jobSide === "taker"
+ nextHit: effect.kind === "modifier" && effect.duration.attacks !== undefined && definition.jobSide === "taker"
};
groupMetadata.set(group, {
ownerSide: skill.side,
effectId: intent.id,
@@ -175,8 +183,9 @@
return {
preBattle,
battleStart,
roundStart,
+ engagement,
attackDeclaredByJobShape,
effectGroups,
damageGroupsByJobShape,
randomness: {
Index: simulator/src/simulator.test.ts
===================================================================
--- simulator/src/simulator.test.ts prev run
+++ simulator/src/simulator.test.ts this run
@@ -20,26 +20,66 @@
function runOnce(input: BattleInput, config: SimulatorConfig, options: SimulationOptions = {}) {
return runPrepared(prepareBattle(input, config), undefined, options);
}
-test("Gordon suppresses on third turns and boosts normal Lancer damage on the following turns", () => {
- const result = runOnce({
- maxRounds: 7,
- attacker: {
- troops: { lancer_t6: 1000 },
- heroes: { Gordon: { skill_1: 2, skill_2: 2, skill_3: 2 } }
- },
- defender: { troops: { lancer_t6: 1000 } }
- }, loadSimulatorConfig(), { mode: "standard" });
+test("Gordon's captured Venom damage matches the Ahmose and Wu Ming interaction", () => {
+ const [input] = JSON.parse(readFileSync(new URL("../../testcases/emulator_verified/ahmose_gordon_renee/ahmose_gordon_vs_wuming.json", import.meta.url), "utf8"));
+ const result = runOnce(input, loadSimulatorConfig());
+ assert.equal(signedRemainingScore(result), 140);
+ for (const [skillId, activations] of [
+ ["ViperFormation", 5],
+ ["VenomInfusion", 34],
+ ["ChemicalTerror", 22],
+ ["ToxicRelease", 17]
+ ] as const) {
+ assert.equal(result.skillReport.attacker.find(skill => skill.skillId === skillId)?.skillActivations, activations, skillId);
+ }
+});
- assert.deepEqual(result.attacks
- .filter((attack) => attack.appliedEffects?.some((effect) => effect.effectId === "ChemicalTerror/1"))
- .map((attack) => attack.round), [4, 7]);
- assert.deepEqual(result.attacks
- .filter((attack) => attack.appliedEffects?.some((effect) => effect.effectId === "ChemicalTerror/2"))
- .map((attack) => attack.round), [3, 6]);
+test("Gordon snapshots Venom damage rather than recalculating it on delivery", () => {
+ const [input] = JSON.parse(readFileSync(new URL("../../testcases/emulator_verified/gordon_isolation_20260925/gordon_solo_1329l_vs_5000l.json", import.meta.url), "utf8"));
+ assert.equal(signedRemainingScore(runOnce(input, loadSimulatorConfig())), -48);
});
+test("Gordon's second-attack Venom damage lands after Chemical Terror first activates", () => {
+ const [input] = JSON.parse(readFileSync(new URL("../../testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_5000l_vs_100l.json", import.meta.url), "utf8"));
+ const result = runOnce(input, loadSimulatorConfig());
+ assert.equal(signedRemainingScore(result), 4998);
+ assert.equal(result.skillReport.attacker.find(skill => skill.skillId === "VenomInfusion")?.skillActivations, 1);
+ assert.equal(result.skillReport.attacker.find(skill => skill.skillId === "ChemicalTerror")?.skillActivations, 1);
+});
+
+test("Venom's delayed hit ignores the Gatot shield that is up when it is calculated", () => {
+ const [input] = JSON.parse(readFileSync(new URL("../../testcases/emulator_verified/model_checks_20260929/c5_gordon_1000l_vs_gatot_3000i.json", import.meta.url), "utf8"));
+ assert.equal(signedRemainingScore(runOnce(input, loadSimulatorConfig())), -2944);
+});
+
+test("Venom shares its triggering hit's next-hit curse without consuming another", () => {
+ const config = loadSimulatorConfig();
+ const probe = (path: string) =>
+ JSON.parse(readFileSync(new URL(`../../testcases/emulator_verified/${path}.json`, import.meta.url), "utf8"))[0];
+ // The infantry series separates sharing the normal hit's curse from reading the fresh one.
+ for (const [path, survivors] of [
+ ["gordon_gwen_t9_probe_20260929/gg_1000l_vs_20000t9", -19522],
+ ["gordon_gwen_t9_probe_20260929/gg_60i_1000l_vs_20000t9", -19352],
+ ["gordon_gwen_t9_probe_20260929/gg_700i_1000l_vs_20000t9", -18333],
+ ["gordon_gwen_t9_probe_20260929/gg_1000i_1000l_vs_20000t9", -17781],
+ ["model_checks_20260929/c8_gordon_gwen_300l_vs_31000t9l", -30939]
+ ] as const) {
+ assert.equal(signedRemainingScore(runOnce(probe(path), config)), survivors, path);
+ }
+
+ const result = runOnce(probe("gordon_gwen_t9_probe_20260929/gg_1000l_vs_20000t9"), config, { mode: "standard" });
+ const hasEagleVision = (attack: { appliedEffects?: AppliedEffect[] } | undefined) =>
+ attack?.appliedEffects?.some(effect => effect.effectId === "EagleVision/1") ?? false;
+ const turn3 = result.attacks.filter(attack => attack.round === 3 && attack.dealerSide === "attacker");
+ const venom = turn3.find(attack => attack.sourceEffectId === "VenomInfusion/1");
+ assert.equal(venom?.calculationRound, 2);
+ assert.equal(hasEagleVision(venom), true);
+ // Venom did not consume the curse its turn-2 attack left, so turn 3's normal hit still gets it.
+ assert.equal(hasEagleVision(turn3.find(attack => attack.dealerUnit === "lancer" && !attack.sourceEffectId)), true);
+});
+
test("Gordon boosts Wayne skill damage on Chemical Terror's triggering turn", () => {
const [input] = JSON.parse(readFileSync(new URL("../../testcases/emulator_verified/gordon_interactions_20260925/gordon_wayne_1200l_vs_4800l.json", import.meta.url), "utf8"));
assert.equal(signedRemainingScore(runOnce(input, loadSimulatorConfig())), -313);
});
@@ -54,8 +94,67 @@
assert.equal(signedRemainingScore(runOnce(input, config)), survivors, path);
}
});
+test("turn-scheduled Thunder Strike survives Ahmose's Infantry pause", () => {
+ const [input] = JSON.parse(readFileSync(new URL("../../testcases/emulator_verified/ahmose_gordon_renee/ahmose_wayne_vs_jasser.json", import.meta.url), "utf8"));
+ const result = runOnce(input, loadSimulatorConfig());
+ assert.equal(signedRemainingScore(result), -141);
+ assert.equal(result.skillReport.attacker.find(skill => skill.skillId === "ViperFormation")?.skillActivations, 92);
+ assert.equal(result.skillReport.attacker.find(skill => skill.skillId === "ThunderStrike")?.skillActivations, 115);
+ const pausedRound = result.attacks.filter(attack => attack.round === 20 && attack.dealerSide === "attacker");
+ assert.equal(pausedRound.find(attack => attack.kind === "normal")?.cancelReason, "no_attack");
+ assert.deepEqual(pausedRound.filter(attack => attack.kind === "skill").map(attack => attack.sourceEffectId), ["ThunderStrike/1"]);
+});
+
+test("Sonya's stun suppresses Hendrik's scheduled strike", () => {
+ const [input] = JSON.parse(readFileSync(new URL("../../testcases/emulator_verified/scheduled_skill_stun_20260925/sonya_600l_vs_hendrik_1200m.json", import.meta.url), "utf8"));
+ const result = runOnce(input, loadSimulatorConfig());
+ assert.equal(signedRemainingScore(result), -422);
+ assert.equal(result.extraSkillAttackJobsByEffect["DragonsHeir/1"], 7);
+ assert.equal(result.attacks.some(attack => attack.round === 6 && attack.dealerSide === "defender" && attack.kind === "skill"), false);
+});
+
+test("an overlapping self-pause cannot override an enemy stun", () => {
+ const result = runOnce({
+ maxRounds: 1,
+ attacker: { troops: { infantry_t1: 1000 }, heroes: { Stop: { skill_1: 1 } } },
+ defender: { troops: { infantry_t1: 1000 }, heroes: { Pause: { skill_1: 1, skill_2: 1 } } }
+ }, minimalConfig({
+ Stop: {
+ name: "Stop",
+ skills: {
+ Stun: {
+ trigger: { type: "battle_start" },
+ effects: { stun: { type: "no_attack", units: { applies_to: "enemy.infantry" }, duration: { turns: { count: 1 } } } }
+ }
+ }
+ },
+ Pause: {
+ name: "Pause",
+ skills: {
+ Rest: {
+ trigger: { type: "battle_start" },
+ effects: { rest: { type: "no_attack", units: { applies_to: "self.infantry" }, duration: { turns: { count: 1 } } } }
+ },
+ Volley: {
+ trigger: { type: "turn" },
+ effects: {
+ volley: {
+ type: "extra_skill_attack", value: 100,
+ units: { applies_to: "self.infantry" },
+ trigger_damage_jobs: [{ source: "use.source", target: "use.target" }]
+ }
+ }
+ }
+ }
+ }
+ }));
+ assert.equal(result.attacks.find(attack => attack.dealerSide === "defender")?.cancelReason, "no_attack");
+ assert.equal(result.attacks.some(attack => attack.dealerSide === "defender" && attack.kind === "skill"), false);
+ assert.equal(result.remaining.attacker.infantry, 1000);
+});
+
test("Renee Dreamslice amplifies allied skill damage against the marked target", () => {
const [input] = JSON.parse(readFileSync(new URL("../../testcases/emulator_verified/ahmose_gordon_renee/ahmose_renee_vs_jasser_mark_kind.json", import.meta.url), "utf8"));
assert.equal(signedRemainingScore(runOnce(input, loadSimulatorConfig())), 937);
});
@@ -64,9 +163,21 @@
const [input] = JSON.parse(readFileSync(new URL("../../testcases/emulator_verified/renee_isolation_20260925/renee_mark_source_death.json", import.meta.url), "utf8"));
assert.equal(signedRemainingScore(runOnce(input, loadSimulatorConfig())), 184);
});
-test("Dream Mark capture leaves next-hit vulnerability for Marksmen and delivery precedes the next ordinary attack", () => {
+test("stun prevents Dream Mark placement but not delivery of an existing mark", () => {
+ const [input] = JSON.parse(readFileSync(new URL("../../testcases/emulator_verified/renee_stun_20260925/renee_300l_vs_sonya_250l.json", import.meta.url), "utf8"));
+ const result = runOnce(input, loadSimulatorConfig());
+ assert.equal(signedRemainingScore(result), 61);
+ const marks = result.attacks.filter(attack => attack.sourceEffectId === "NightmareTrace/1");
+ assert.equal(marks.some(attack => attack.round === 7), false);
+ assert.ok(marks.some(attack => attack.round === 11 && attack.kills > 0));
+ assert.equal(result.attacks.find(attack =>
+ attack.round === 11 && attack.dealerSide === "attacker" && !attack.sourceEffectId
+ )?.cancelReason, "no_attack");
+});
+
+test("Dream Mark is the turn-start event that consumes the pending next-hit curse and lands before the next turn's attacks", () => {
const input: BattleInput = {
maxRounds: 3,
attacker: {
troops: { lancer_t6: 100000, marksman_t6: 100000 },
@@ -74,22 +185,49 @@
},
defender: { troops: { infantry_t6: 1000000 }, heroes: {} }
};
const result = runOnce(input, loadSimulatorConfig(), { mode: "trace" });
- const marksmanHit = result.attacks.find(attack =>
- attack.round === 2 && attack.dealerSide === "attacker" && attack.dealerUnit === "marksman"
- )!;
- assert.ok(marksmanHit.appliedEffects?.some(effect => effect.effectId === "EagleVision/1"));
- const nextTurn = result.attacks.filter(attack => attack.round === 3 && attack.dealerSide === "attacker");
- assert.equal(nextTurn[0].sourceEffectId, "NightmareTrace/1");
- assert.ok(nextTurn[0].appliedEffects?.some(effect => effect.effectId === "EagleVision/1"));
- assert.equal(nextTurn[1].sourceEffectId, undefined);
- assert.equal(nextTurn[1].appliedEffects?.some(effect => effect.effectId === "EagleVision/1"), false);
+ const hasEagleVision = (attack: { appliedEffects?: AppliedEffect[] } | undefined) =>
+ attack?.appliedEffects?.some(effect => effect.effectId === "EagleVision/1") ?? false;
+ const turn2 = result.attacks.filter(attack => attack.round === 2 && attack.dealerSide === "attacker");
+ // The turn-1 marksman curse is taken by Renee's turn-start event, not by the first ordinary hit.
+ assert.equal(hasEagleVision(turn2.find(attack => attack.dealerUnit === "lancer" && !attack.sourceEffectId)), false);
+ assert.equal(hasEagleVision(turn2.find(attack => attack.dealerUnit === "marksman")), true);
+ const turn3 = result.attacks.filter(attack => attack.round === 3 && attack.dealerSide === "attacker");
+ assert.equal(turn3[0].sourceEffectId, "NightmareTrace/1");
+ assert.equal(turn3[0].calculationRound, 2);
+ assert.equal(hasEagleVision(turn3[0]), true);
+ assert.equal(hasEagleVision(turn3.find(attack => attack.dealerUnit === "lancer" && !attack.sourceEffectId)), true);
});
-test("delayed damage settles shields on arrival in recorded Gatot battles", () => {
+test("Renee and Gwen per-turn probes match the game when infantry shift the curse chain", () => {
const config = loadSimulatorConfig();
for (const [path, survivors] of [
+ ["rg_1000l_vs_20000t9", -18899],
+ ["rg_30i_1000l_vs_20000t9", -18753],
+ ["rg_60i_1000l_vs_20000t9", -18616],
+ ["rg_200i_1000l_vs_20000t9", -18241],
+ ["rg_400i_1000l_vs_20000t9", -17648]
+ ] as const) {
+ const [input] = JSON.parse(readFileSync(new URL(`../../testcases/emulator_verified/renee_gwen_t9_probe_20260929/${path}.json`, import.meta.url), "utf8"));
+ assert.equal(signedRemainingScore(runOnce(input, config)), survivors, path);
+ }
+});
+
+test("Dream Mark damage lands before Gatot's next shield activates", () => {
+ const config = loadSimulatorConfig();
+ for (const [path, survivors] of [
+ ["renee_shield_saturation_20260926/renee_100l_vs_gatot_50i.json", -37],
+ ["renee_shield_saturation_20260926/renee_100l_vs_gatot_100i.json", -91],
+ ["gwen_renee_wall_20260926/renee_500l_vs_gatot_300i.json", -229]
+ ] as const) {
+ const [input] = JSON.parse(readFileSync(new URL(`../../testcases/emulator_verified/${path}`, import.meta.url), "utf8"));
+ assert.equal(signedRemainingScore(runOnce(input, config)), survivors, path);
+ }
+});
+test("delayed damage never meets shields in recorded Gatot battles", () => {
+ const config = loadSimulatorConfig();
+ for (const [path, survivors] of [
["renee_s2_bucket_confirmations/gatot_shield_500_150.json", 157],
["renee_s2_families/defense_gatot.json", 339]
] as const) {
const [input] = JSON.parse(readFileSync(new URL(`../../testcases/emulator_verified/${path}`, import.meta.url), "utf8"));
@@ -991,9 +1129,9 @@
assert.equal(round4LancerAttack?.takerUnit, "marksman");
assert.equal((round4LancerAttack?.appliedEffects ?? []).some((effect) => effect.effectId === "mark"), false);
});
-test("carried extra damage expires when the next normal attack does not match its locked target", () => {
+test("turn-start delayed damage lands on its marked target regardless of the next normal target", () => {
const result = runOnce(
{
maxRounds: 3,
attacker: {
@@ -1045,11 +1183,13 @@
{ mode: "trace" }
);
const lancerAttacks = result.attacks.filter((attack) => attack.dealerSide === "attacker" && attack.dealerUnit === "lancer");
- assert.equal(lancerAttacks.find((attack) => attack.round === 2 && attack.kind === "normal")?.takerUnit, "marksman");
- assert.equal(lancerAttacks.find((attack) => attack.round === 3 && attack.kind === "normal")?.takerUnit, "infantry");
- assert.equal(lancerAttacks.some((attack) => attack.sourceEffectId === "delayed"), false);
+ const ordinary = (round: number) => lancerAttacks.find((attack) => attack.round === round && attack.kind === "normal" && !attack.sourceEffectId);
+ assert.equal(ordinary(2)?.takerUnit, "marksman");
+ assert.equal(ordinary(3)?.takerUnit, "infantry");
+ const delayed = lancerAttacks.filter((attack) => attack.sourceEffectId === "delayed");
+ assert.deepEqual(delayed.map((attack) => [attack.round, attack.calculationRound, attack.takerUnit]), [[3, 2, "marksman"]]);
});
test("carried-damage fixture snapshots normal damage on the even turn and only delivers it next turn", () => {
const result = runOnce(
@@ -3107,10 +3247,41 @@
);
assert.deepEqual(result.extraSkillAttackJobsByEffect, { hitAgain: 1 });
});
+test("engagement effects with engaged_with activate only for the line's engaged class", () => {
+ const result = runOnce(
+ {
+ maxRounds: 1,
+ attacker: { troops: { lancer_t1: 1000 }, heroes: { ClassBonus: { skill_1: 1 } } },
+ defender: { troops: { infantry_t1: 1000, lancer_t1: 1000 }, heroes: {} }
+ },
+ minimalConfig({
+ ClassBonus: {
+ name: "ClassBonus",
+ troop_type: "lancer",
+ skills: {
+ PowerShot: {
+ trigger: { type: "engagement", source: "any", target: ["lancer", "infantry"] },
+ effects: {
+ vsLancer: { type: "type.single_target.damage.up", value: 40, engaged_with: ["lancer"], units: { applies_to: "trigger.source" }, duration: { turns: { count: 1 } } },
+ vsInfantry: { type: "type.single_target.damage.up", value: 25, engaged_with: ["infantry"], units: { applies_to: "trigger.source" }, duration: { turns: { count: 1 } } }
+ }
+ }
+ }
+ }
+ }),
+ { mode: "trace" }
+ );
+ const hit = result.attacks.find(attack => attack.dealerSide === "attacker" && attack.kind === "normal")!;
+ assert.equal(hit.takerUnit, "infantry");
+ const applied = (hit.appliedEffects ?? []).map(effect => effect.effectId);
+ assert.ok(applied.includes("vsInfantry"));
+ assert.equal(applied.includes("vsLancer"), false);
+});
+
for (const delayed of [false, true]) {
- test(`extra hits apply target modifiers to each actual recipient (${delayed ? "captured" : "immediate"})`, () => {
+ test(`an engagement bonus covers all of the line's hits while each recipient's own modifiers apply (${delayed ? "delayed" : "immediate"})`, () => {
const splash: Omit<EffectIntentDefinition, "id"> = {
type: "extra_skill_attack",
value: 100,
units: {
@@ -3122,22 +3293,27 @@
};
const result = runOnce(
{
maxRounds: delayed ? 2 : 1,
- attacker: { troops: { marksman_t1: 1000000 }, heroes: { MultiTarget: { skill_1: 1, skill_2: 1 } } },
+ attacker: { troops: { marksman_t1: 1000000 }, heroes: { MultiTarget: { skill_1: 1, skill_2: 1, skill_3: 1 } } },
defender: { troops: { infantry_t1: 1000000, lancer_t1: 1000000, marksman_t1: 1000000 }, heroes: {} }
},
minimalConfig({
MultiTarget: {
name: "MultiTarget",
troop_type: "marksman",
skills: {
+ InfantryEngagement: {
+ trigger: { type: "engagement", source: "marksman", target: "infantry" },
+ effects: {
+ infantryBonus: { type: "type.single_target.damage.up", value: 25, units: { applies_to: "trigger.source" }, duration: { turns: { count: 1 } } }
+ }
+ },
TargetModifiers: {
trigger: { type: "battle_start" },
effects: {
- infantryBonus: { type: "type.single_target.damage.up", value: 25, units: { applies_vs: "enemy.infantry" } },
- lancerBonus: { type: "type.single_target.damage.up", value: 40, units: { applies_vs: "enemy.lancer" } },
- marksmanPenalty: { type: "type.single_target.damage.down", value: 100, units: { applies_vs: "enemy.marksman" } }
+ lancerVulnerability: { type: "active.hero.damageTaken.up", value: 40, units: { applies_to: "enemy.lancer" } },
+ marksmanResistance: { type: "active.hero.damageTaken.down", value: 100, units: { applies_to: "enemy.marksman" } }
}
},
Splash: {
trigger: { type: "turn", first: 1, every: 10 },
@@ -3159,14 +3335,15 @@
assert.ok(parent);
assert.equal(parent.takerUnit, "infantry");
const hits = result.attacks.filter(attack => attack.sourceEffectId === "splash");
assert.deepEqual(hits.map(hit => hit.takerUnit), UNIT_TYPES);
- const factors = { infantry: 1.25, lancer: 1.4, marksman: 0.5 };
+ // The Marksmen are engaged with Infantry this turn, so the +25% reaches every recipient.
+ const factors = { infantry: 1, lancer: 1.4, marksman: 0.5 };
for (const hit of hits) {
assert.equal(hit.round, delayed ? 2 : 1);
assert.ok(
- Math.abs(hit.kills - parent.kills * factors[hit.takerUnit] / 1.25) < 1e-9,
- `${hit.takerUnit} casualties must use that recipient's modifiers`
+ Math.abs(hit.kills - parent.kills * factors[hit.takerUnit]) < 1e-9,
+ `${hit.takerUnit} casualties must keep the attack's class bonus and use that recipient's modifiers`
);
}
});
}
Index: simulator/src/simulator.ts
===================================================================
--- simulator/src/simulator.ts prev run
+++ simulator/src/simulator.ts this run
@@ -14,12 +14,13 @@
SimulatorConfig,
UnitType
} from "./types";
import { UNIT_TYPES, unitMaskHas } from "./types";
-import { generateDamageJob, deliverDamageJob, ceilIgnoringFloatResidue, minInitialArmy, type DamageResult } from "./damage";
+import { appliedNextHitModifiers, generateDamageJob, deliverDamageJob, ceilIgnoringFloatResidue, minInitialArmy, type DamageResult, type NextHitModifier } from "./damage";
import { createRecorder, type BattleRecorder } from "./recorder";
import {
activateEffect,
+ compiledTriggerForSkill,
advanceEffectAttackDelay,
createSeededRng,
hasAttackDurationConstraint,
isEffectAttackReady,
@@ -30,8 +31,9 @@
import { buildRuntimeSkills, type RuntimeSkills } from "./runtimeSkills";
import { activatePreBattleEffects, buildResolved, prepareBattle, type CompiledBattle } from "./prepare";
import {
addActiveEffect,
+ expireActiveEffect,
capJobToRemainingTarget,
chargeEffectUse,
chargeUsedEffects,
chargeUsedEffectsForJob,
@@ -39,17 +41,18 @@
emptyRoundTargetDamage,
materializeDeferredEffects,
materializeTriggeredEffects,
preparedChancePasses,
+ activateScheduledShields,
processEffectSchedule,
targetExhausted,
triggerAttackSkills,
triggerSkills,
type DamageJobResult,
type RunLoopOptions,
type Runtime
} from "./runtime";
-import { captureTriggeredExtraDamage, processExtraAttacks } from "./extraAttacks";
+import { calculateDelayedDamage, landDelayedDamage, processExtraAttacks } from "./extraAttacks";
// Re-exported so the public battle API stays importable from one module.
export { prepareBattle, type CompiledBattle } from "./prepare";
import { emptyTroops, resolveFighter } from "./fighterResolution";
@@ -78,8 +81,9 @@
interface Control {
effect: ActiveEffect;
reason: "dodge" | "no_attack";
attackDurationEffects: ActiveEffect[];
+ blocksTurnAttacks: boolean;
}
function bearFighterInput(): FighterInput {
return {
@@ -249,15 +253,25 @@
for (let round = 1; round <= maxRounds; round += 1) {
if (winnerFor(runtime.troops)) break;
rounds = round;
const roundStartTroops = snapshotTroops(runtime.troops);
- processEffectSchedule(runtime, round);
- triggerRoundStartSkills(round, runtime, recorder);
-
const intents: AttackIntent[] = [];
const results: DamageJobResult[] = [];
const cancelled: CancelledAttack[] = [];
const roundTargetDamage = emptyRoundTargetDamage();
+
+ // Turn start: delayed hits settled earlier apply their kills; last turn's effects expire
+ // and this turn's activate; turn-trigger skills act; then scheduled shields go up.
+ landDelayedDamage(round, runtime, roundStartTroops, roundTargetDamage, loopOptions, recorder, results);
+ if (loopOptions.scoreSide) score += scoreFor(results, loopOptions.scoreSide);
+ // Attacks are still declared against targets alive in the turn's snapshot; landed
+ // kills only cap what those attacks can remove.
+ const landedTargetDamage = snapshotTroops(roundTargetDamage);
+ processEffectSchedule(runtime, round);
+ triggerRoundStartSkills(round, runtime, recorder);
+ activateEngagementSkills(round, runtime, recorder, roundStartTroops);
+ fireTurnStartCarriers(round, fighters, runtime, recorder, damageJobOptions, roundStartTroops);
+ activateScheduledShields(runtime, round);
// Resolve each normal attack as one procedural cluster. Later attacks observe effects
// produced by earlier attacks; no synthetic global attack-declaration phase exists.
const orderIndexBySide: Record<SideId, number> = { attacker: 0, defender: 0 };
for (const dealerUnit of UNIT_TYPES) {
@@ -284,9 +298,9 @@
recorder.recordBattleOrder(intent, ordered.effect, takerUnit);
}
const job = normalJob(intent, roundStartTroops);
- if (loopOptions.capRoundKills && targetExhausted(job, roundStartTroops, roundTargetDamage)) continue;
+ if (loopOptions.capRoundKills && targetExhausted(job, roundStartTroops, roundTargetDamage, landedTargetDamage)) continue;
// A pre-existing no_attack prevents the attack from being declared at all.
// Reactive dodge skills instead roll on this declaration and can dodge this job.
const noAttack = applicableControl(job, runtime, "no_attack");
@@ -295,16 +309,17 @@
runtime.attackControlCounts.no_attack += 1;
materializeTriggeredEffects(control.effect, round, intent, runtime, recorder);
if (useEffectsOnCancel.no_attack) chargeCancelledAttack(job, control.effect, control.attackDurationEffects, runtime);
cancelled.push({ intent, control });
+ if (!control.blocksTurnAttacks) {
+ const extraAttacks = processExtraAttacks("cancelled", job, intent, runtime, fighters, damageJobOptions, roundTargetDamage, loopOptions, results);
+ if (loopOptions.scoreSide && job.dealerSide === loopOptions.scoreSide.dealerSide && job.takerSide === loopOptions.scoreSide.takerSide) {
+ score += extraAttacks.totalKills;
+ }
+ }
continue;
}
- const pendingAttacks = processExtraAttacks("pending", job, intent, runtime, fighters, damageJobOptions, roundTargetDamage, loopOptions, results);
- if (loopOptions.scoreSide && job.dealerSide === loopOptions.scoreSide.dealerSide && job.takerSide === loopOptions.scoreSide.takerSide) {
- score += pendingAttacks.totalKills;
- }
-
const matchingTriggerSkills = runtime.skills.attackDeclaredByJobShape[
damageJobShapeSlot("normal", intent.dealerSide, intent.dealerUnit, intent.takerSide, intent.takerUnit)
];
const deferredEffects = matchingTriggerSkills
@@ -313,8 +328,9 @@
const dodge = applicableControl(job, runtime, "dodge");
const triggeredChildren = fireApplicableCarriers(round, job, intent, runtime, recorder);
let normalKills = 0;
+ let normalNextHit: NextHitModifier[] = [];
if (dodge) {
runtime.attackControlCounts.dodge += 1;
materializeTriggeredEffects(dodge.effect, round, intent, runtime, recorder);
if (useEffectsOnCancel.dodge) chargeCancelledAttack(job, dodge.effect, dodge.attackDurationEffects, runtime);
@@ -326,13 +342,16 @@
normalKills = normalResult.kills;
if (loopOptions.scoreSide && job.dealerSide === loopOptions.scoreSide.dealerSide && job.takerSide === loopOptions.scoreSide.takerSide) {
score += normalResult.kills;
}
+ // Read before charging: expiring next-hit effects leave their groups.
+ normalNextHit = appliedNextHitModifiers(runtime.usedEffects, round);
chargeUsedEffectsForJob(runtime, job, recorder);
results.push({ job, result: normalResult, intent });
}
- captureTriggeredExtraDamage(triggeredChildren, job, fighters, damageJobOptions, runtime);
+ // Extra damage attached to this attack shares its normal hit's next-hit modifiers.
+ calculateDelayedDamage(triggeredChildren, job, fighters, damageJobOptions, runtime, normalNextHit);
advanceNormalAttackCounters(intent, runtime);
const extraAttacks = processExtraAttacks("immediate", job, intent, runtime, fighters, damageJobOptions, roundTargetDamage, loopOptions, results);
if (loopOptions.scoreSide && job.dealerSide === loopOptions.scoreSide.dealerSide && job.takerSide === loopOptions.scoreSide.takerSide) {
score += extraAttacks.totalKills;
@@ -477,17 +496,20 @@
reason: Control["reason"]
): Control | undefined {
const attackDurationEffects: ActiveEffect[] = [];
let selected: Control | undefined;
+ let blocksTurnAttacks = false;
for (const effect of runtime.effectIndex.controls) {
if (controlType(effect) !== reason) continue;
if (!controlEffectApplies(effect, job, reason)) continue;
if (!advanceEffectAttackDelay(effect)) continue;
if (hasAttackDurationConstraint(effect)) attackDurationEffects.push(effect);
+ blocksTurnAttacks ||= reason === "no_attack" && effect.ownerSide !== job.dealerSide;
selected = {
effect,
reason,
- attackDurationEffects
+ attackDurationEffects,
+ blocksTurnAttacks
};
}
return selected;
}
@@ -537,8 +559,98 @@
}
return children;
}
+// Each living line settles its target at turn start (after turn skills such as Ambusher reorder
+// it). Engagement skills fire for lines engaged with their trigger target, e.g. Charge for
+// Lancers engaging Marksmen; their effects cover the line's whole turn.
+function activateEngagementSkills(
+ round: number,
+ runtime: Runtime,
+ recorder: BattleRecorder,
+ roundStartTroops: DamageJob["roundStartTroops"]
+): void {
+ for (const { skill, probabilityPct } of runtime.skills.engagement) {
+ const trigger = compiledTriggerForSkill(skill);
+ const side = trigger.source.side;
+ for (const dealerUnit of UNIT_TYPES) {
+ if (!unitMaskHas(trigger.source.units, dealerUnit)) continue;
+ const takerUnit = engagedTarget(dealerUnit, side, runtime, roundStartTroops);
+ if (!takerUnit || trigger.target.side !== oppositeSide(side) || !unitMaskHas(trigger.target.units, takerUnit)) continue;
+ recorder.recordSkillTriggerAttempt(skill);
+ if (!preparedChancePasses(probabilityPct, runtime.rng)) continue;
+ recorder.recordSkillTriggered(skill);
+ const intent = makeNormalIntent(round, side, dealerUnit, oppositeSide(side), takerUnit, 0, runtime);
+ for (const effectIntent of skill.effects) {
+ if (effectIntent.engaged_with && !effectIntent.engaged_with.includes(takerUnit)) continue;
+ addActiveEffect(runtime, activateEffect(skill, effectIntent, round, intent));
+ runtime.effectActivationCounts[skill.side] += 1;
+ recorder.recordSkillEffectActivated(skill);
+ }
+ }
+ }
+}
+
+/** The unit a living line targets this turn, as its normal attack will; undefined if it cannot act. */
+function engagedTarget(
+ dealerUnit: UnitType,
+ side: SideId,
+ runtime: Runtime,
+ roundStartTroops: DamageJob["roundStartTroops"]
+): UnitType | undefined {
+ if (ceilIgnoringFloatResidue(roundStartTroops[side][dealerUnit] ?? 0) <= 0) return undefined;
+ const ordered = orderFromEffects(dealerUnit, side, runtime.effectIndex, false);
+ return firstLivingUnit(ordered?.order ?? UNIT_TYPES, oppositeSide(side), roundStartTroops);
+}
+
+// Carriers created by turn-triggered skills act at turn start, before any attack: the first
+// living unit they apply to uses them at once against its current target, so a delayed hit
+// they spawn is the next damage event for any live next-hit modifier. A unit under an enemy
+// no_attack control cannot act, and an unused turn carrier is discarded either way.
+function fireTurnStartCarriers(
+ round: number,
+ fighters: Record<SideId, ResolvedFighter>,
+ runtime: Runtime,
+ recorder: BattleRecorder,
+ damageJobOptions: Parameters<typeof calculateDelayedDamage>[3],
+ roundStartTroops: DamageJob["roundStartTroops"]
+): void {
+ const carriers = runtime.effectIndex.carriers.filter((carrier) =>
+ carrier.createdRound === round && carrier.sourceSkill?.trigger.type === "turn"
+ );
+ for (const carrier of carriers) {
+ const side = carrier.appliesTo.side;
+ const dealerUnit = UNIT_TYPES.find((unit) =>
+ unitMaskHas(carrier.appliesTo.units, unit) && ceilIgnoringFloatResidue(roundStartTroops[side][unit] ?? 0) > 0
+ );
+ const takerSide = oppositeSide(side);
+ const takerUnit = dealerUnit ? engagedTarget(dealerUnit, side, runtime, roundStartTroops) : undefined;
+ if (!dealerUnit || !takerUnit || !unitMaskHas(carrier.appliesVs.units, takerUnit) || blockedByEnemyControl(runtime, side, dealerUnit)) {
+ expireActiveEffect(runtime, carrier);
+ continue;
+ }
+ const intent = makeNormalIntent(round, side, dealerUnit, takerSide, takerUnit, 0, runtime);
+ const children = materializeTriggeredEffects(carrier, round, intent, runtime, recorder);
+ chargeEffectUse(runtime, carrier);
+ calculateDelayedDamage(children, normalJob(intent, roundStartTroops), fighters, damageJobOptions, runtime);
+ }
+}
+
+function blockedByEnemyControl(runtime: Runtime, side: SideId, unit: UnitType): boolean {
+ return runtime.effectIndex.controls.some((effect) =>
+ controlType(effect) === "no_attack" && effect.ownerSide !== side &&
+ effect.appliesTo.side === side && unitMaskHas(effect.appliesTo.units, unit) && isEffectAttackReady(effect)
+ );
+}
+
+function scoreFor(results: DamageJobResult[], scoreSide: NonNullable<RunLoopOptions["scoreSide"]>): number {
+ let kills = 0;
+ for (const { job, result } of results) {
+ if (job.dealerSide === scoreSide.dealerSide && job.takerSide === scoreSide.takerSide) kills += result.kills;
+ }
+ return kills;
+}
+
function advanceNormalAttackCounters(intent: AttackIntent, runtime: Runtime): void {
runtime.counters.attacks[intent.dealerSide][intent.dealerUnit] += 1;
runtime.counters.received[intent.takerSide][intent.takerUnit] += 1;
}
Index: simulator/src/tooling/testcases.test.ts
===================================================================
--- simulator/src/tooling/testcases.test.ts prev run
+++ simulator/src/tooling/testcases.test.ts this run
@@ -25,44 +25,49 @@
assert.ok(files.some((file) => file.endsWith("emulator_verified/jasser_solo.json.disabled")));
assert.ok(files.some((file) => file.endsWith("emulator_verified/reina_logan_combo_v2.json.stale_troops")));
});
-test("discoverTestcaseFiles matches filenames or heroes on either side, including joiners", (t) => {
- const testcaseRoot = tempDir("simulator-hero-matching");
+test("discoverTestcaseFiles matches filenames and participating heroes, but not directories or descriptions", (t) => {
+ const testcaseRoot = tempDir("simulator-directory-matching");
t.after(() => rmSync(testcaseRoot, { recursive: true, force: true }));
const fixtures = {
"attacker.json": { attacker: { heroes: { Gwen: {} } } },
"defender.json": [{}, { defender: { heroes: { Gwen: {} } } }],
"attacker-joiner.json": { attacker: { joiner_heroes: { Gwen: {} } } },
"defender-joiner.json": { defender: { joiner_heroes: { Gwen: {} } } },
+ "array-heroes.json": { attacker: { heroes: [{ name: "Gwen" }] } },
+ "array-joiners.json": { defender: { joiner_heroes: [{ name: "Gwen" }] } },
"unrelated.json": { description: "Gwen", attacker: { heroes: { Sergey: {} } } },
- "disabled.json.disabled": { attacker: { heroes: { Gwen: {} } } },
- "stale.json.stale_troops": { defender: { heroes: { Gwen: {} } } },
+ "Gwen-disabled.json.disabled": { attacker: { heroes: { Gwen: {} } } },
+ "Gwen-stale.json.stale_troops": { defender: { heroes: { Gwen: {} } } },
};
for (const [file, entry] of Object.entries(fixtures)) {
writeFileSync(resolve(testcaseRoot, file), JSON.stringify(entry));
}
writeFileSync(resolve(testcaseRoot, "Gwen-invalid.json"), "{");
writeFileSync(resolve(testcaseRoot, "invalid.json"), "{");
- const expected = [
- "Gwen-invalid.json", "attacker-joiner.json", "attacker.json",
- "defender-joiner.json", "defender.json",
- ];
+ mkdirSync(resolve(testcaseRoot, "Gwen"));
+ writeFileSync(resolve(testcaseRoot, "Gwen", "other.json"), "{}");
+ writeFileSync(resolve(testcaseRoot, "Gwen", "Gwen-nested.json"), "{}");
+
+ const expected = ["Gwen-invalid.json", "Gwen/Gwen-nested.json", "attacker.json", "defender.json",
+ "attacker-joiner.json", "defender-joiner.json", "array-heroes.json", "array-joiners.json"];
assert.deepEqual(
- discoverTestcaseFiles({ testcaseRoot, matching: "Gwe" }),
+ discoverTestcaseFiles({ testcaseRoot, matching: "gwe" }),
expected.map((file) => resolve(testcaseRoot, file)).sort(),
);
assert.deepEqual(
discoverTestcaseFiles({ testcaseRoot, matching: "Gwe", includeDisabled: true }),
- [...expected, "disabled.json.disabled", "stale.json.stale_troops"]
+ [...expected, "Gwen-disabled.json.disabled", "Gwen-stale.json.stale_troops"]
.map((file) => resolve(testcaseRoot, file)).sort(),
);
assert.deepEqual(
discoverTestcaseFiles({ testcaseRoot, matching: "unrelated" }),
[resolve(testcaseRoot, "unrelated.json")],
);
assert.deepEqual(discoverTestcaseFiles({ testcaseRoot, matching: "missing" }), []);
+ assert.deepEqual(discoverTestcaseFiles({ testcaseRoot, matching: "simulator-directory-matching" }), []);
});
test("runTestcases returns compact summary entries and full detail entries separately", () => {
const config = loadSimulatorConfig();
Index: simulator/src/tooling/testcases.ts
===================================================================
--- simulator/src/tooling/testcases.ts prev run
+++ simulator/src/tooling/testcases.ts this run
@@ -1,6 +1,6 @@
import { readdirSync, readFileSync, statSync } from "node:fs";
-import { dirname, relative, resolve } from "node:path";
+import { basename, dirname, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import {
loadCalibrationComparison,
@@ -199,34 +199,38 @@
export function discoverTestcaseFiles(options: Pick<TestcaseRunOptions, "testcaseRoot" | "matching" | "includeDisabled"> = {}): string[] {
const root = resolve(options.testcaseRoot ?? defaultTestcaseRoot());
const files: string[] = [];
walk(root, files);
+ const matching = options.matching?.toLowerCase();
return files
.filter((file) => isDiscoverableTestcaseFile(file, options.includeDisabled))
.filter((file) => options.includeDisabled || (!file.endsWith(".disabled") && !file.endsWith(".stale_troops")))
- .filter((file) => !options.matching || file.includes(options.matching) || fileHeroesMatch(file, options.matching))
+ .filter((file) => {
+ if (!matching || basename(file).toLowerCase().includes(matching)) return true;
+ let parsed: unknown;
+ try {
+ parsed = JSON.parse(readFileSync(file, "utf8"));
+ } catch {
+ return false;
+ }
+ const entries = Array.isArray(parsed) ? parsed : [parsed];
+ return entries.some((entry) => {
+ const testcase = asObject(entry);
+ for (const side of ["attacker", "defender"]) {
+ const fighter = asObject(testcase[side]);
+ for (const collection of [fighter.heroes, fighter.joiner_heroes]) {
+ const names = Array.isArray(collection)
+ ? collection.map((hero) => asObject(hero).name)
+ : Object.keys(asObject(collection));
+ if (names.some((name) => typeof name === "string" && name.toLowerCase().includes(matching))) return true;
+ }
+ }
+ return false;
+ });
+ })
.sort();
}
-function fileHeroesMatch(file: string, matching: string): boolean {
- let parsed: unknown;
- try {
- parsed = JSON.parse(readFileSync(file, "utf8"));
- } catch {
- return false;
- }
- const entries = Array.isArray(parsed) ? parsed : [parsed];
- return entries.some((entry) => {
- const testcase = asObject(entry);
- return [testcase.attacker, testcase.defender].some((side) => {
- const army = asObject(side);
- return [army.heroes, army.joiner_heroes].some((heroes) =>
- Object.keys(asObject(heroes)).some((name) => name.includes(matching)),
- );
- });
- });
-}
-
export function runTestcases(options: TestcaseRunOptions, config: SimulatorConfig): TestcaseRunReport {
const prepared = prepareTestcaseCases(options);
return runPreparedTestcases(options, config, prepared, (job) => executeTestcaseCase(job, config));
}
@@ -483,20 +487,25 @@
averageRounds: number;
deterministic: boolean;
thresholds?: Record<string, number>;
exact?: boolean;
+ maxAdjustment?: number;
}): InternalStatAdjustment | undefined {
+ // The search value is the shift of the finest-rounded stat; each coarser stat moves in
+ // proportion to its own rounding bound (see statRoundingBound).
let maxAdjustment = STAT_ROUNDING_MAX_ADJUSTMENT;
for (const fighter of [options.input.attacker, options.input.defender]) {
for (const stats of Object.values(fighter.stats ?? {}) as Array<Partial<StatBlock>>) {
for (const key of ["attack", "defense", "lethality", "health"] as const) {
const value = stats[key];
if (value === undefined) continue;
- if (Math.abs(value * 100 - Math.round(value * 100)) > 1e-9) return undefined;
- if (Math.abs(value * 10 - Math.round(value * 10)) > 1e-9) maxAdjustment = 0.005;
+ const bound = statRoundingBound(value);
+ if (bound === undefined) return undefined;
+ maxAdjustment = Math.min(maxAdjustment, bound);
}
}
}
+ options = { ...options, maxAdjustment };
// Deterministic cases correct any nonzero bias; stochastic cases correct only outright misses.
// Either way a nonzero bias is needed to pick a search direction.
const shouldCorrect = options.deterministic ? options.game.bias_raw !== 0 : !options.game.passes;
if (!shouldCorrect || options.game.bias_raw === 0) return undefined;
@@ -539,10 +548,11 @@
averageRounds: number;
deterministic: boolean;
thresholds?: Record<string, number>;
exact?: boolean;
+ maxAdjustment?: number;
}, value: number): InternalStatAdjustment {
- const adjustedInput = inputWithStatAdjustment(options.input, value);
+ const adjustedInput = inputWithStatAdjustment(options.input, value, options.maxAdjustment ?? STAT_ROUNDING_MAX_ADJUSTMENT);
const candidateSamples = simulateAdjustedOutcomes(adjustedInput, options.job, options.config);
const adjusted = compareOutcomeDistribution({
candidate: { samples: candidateSamples },
reference: { samples: options.reference },
@@ -596,24 +606,31 @@
}
return samples;
}
-function inputWithStatAdjustment(input: BattleInput, value: number): BattleInput {
+function inputWithStatAdjustment(input: BattleInput, value: number, finestBound: number): BattleInput {
const adjusted = structuredClone(input);
- adjustFighterStats(adjusted.attacker, value);
- adjustFighterStats(adjusted.defender, -value);
+ adjustFighterStats(adjusted.attacker, value, finestBound);
+ adjustFighterStats(adjusted.defender, -value, finestBound);
return adjusted;
}
-function adjustFighterStats(fighter: FighterInput, value: number): void {
+function adjustFighterStats(fighter: FighterInput, value: number, finestBound: number): void {
for (const stats of Object.values(fighter.stats ?? {}) as Array<Partial<StatBlock>>) {
for (const key of ["attack", "defense", "lethality", "health"] as Array<keyof StatBlock>) {
if (stats[key] === undefined) continue;
- stats[key] = roundStatAdjustment(Number(stats[key]) + value);
+ const bound = statRoundingBound(Number(stats[key])) ?? finestBound;
+ stats[key] = roundStatAdjustment(Number(stats[key]) + value * (bound / finestBound));
}
}
}
+/** Half the display step of a stat shown to at most two decimals; undefined for finer values. */
+function statRoundingBound(value: number): number | undefined {
+ if (Math.abs(value * 100 - Math.round(value * 100)) > 1e-9) return undefined;
+ return Math.abs(value * 10 - Math.round(value * 10)) > 1e-9 ? 0.005 : STAT_ROUNDING_MAX_ADJUSTMENT;
+}
+
function adjustmentMode(adjusted: ParityComparisonMetrics, deterministic: boolean): TestcaseStatAdjustment["mode"] {
if (deterministic) {
if (adjusted.bias_raw === 0) return "deterministic_exact";
if (Math.abs(adjusted.bias_raw) <= 1) return "deterministic_within_one";
Index: simulator/src/types.ts
===================================================================
--- simulator/src/types.ts prev run
+++ simulator/src/types.ts this run
@@ -1,6 +1,4 @@
-import type { GeneratedDamage } from "./damage";
-
export type SideId = "attacker" | "defender";
export type UnitType = "infantry" | "lancer" | "marksman";
export type DamageKind = "normal" | "skill";
export type UnitMask = number;
@@ -96,8 +94,10 @@
/** Restrict this modifier to these damage job kinds without changing its arithmetic bucket. */
applies_to_damage_kinds?: DamageKind[];
/** Apply this modifier only while the named effect is applicable to the same damage job. */
requires_effect?: string;
+ /** Engagement skills only: activate this effect only for a line engaged with one of these units. */
+ engaged_with?: UnitType[];
value_formula?: PercentOfValueFormula;
value_evolution?: { type?: string; step?: string; value?: number };
units?: Record<string, unknown>;
trigger_damage_jobs?: TriggerDamageJobDefinition[];
@@ -289,10 +289,11 @@
// Index into RuntimeSkills.effectGroups and every runtime's liveEffectsByGroup.
ordinal: number;
bucketIndex: number;
sameEffectStacking: SameEffectStacking;
- /** Taker-side attack budgets are sampled when captured damage lands, not when it is generated. */
- attackLimitedTakerModifier: boolean;
+ // Attack-count-limited target-side modifier (e.g. Gwen's Eagle Vision): a "next hit"
+ // debuff consumed by the damage events it applies to.
+ nextHit?: boolean;
// Prepared dependency lookup indexed by damage-job shape. An empty slot means the
// required effect cannot apply to that job, so this group must not contribute.
requiredGroupOrdinalsByJobShape?: Array<number[] | undefined>;
}
@@ -329,10 +330,8 @@
// unless useEffectsOnDodge/useEffectsOnNoAttack disable that.
uses: number;
sameEffectStacking: SameEffectStacking;
triggerEffects?: ResolvedEffectIntentDefinition[];
- /** Generation-time damage awaiting shield settlement on its later matching attack. */
- pendingDamageJobs?: PendingDamageJob[];
effectGroup?: ActiveEffectGroup;
effectGroupPosition?: number;
}
@@ -377,13 +376,8 @@
appliedEffects?: AppliedEffect[];
trace?: DamageEquationTrace;
}
-export interface PendingDamageJob {
- job: DamageJob;
- generated: GeneratedDamage;
-}
-
export interface CounterDelta {
side: SideId;
unit: UnitType;
counter: "attacks" | "received_attacks";
Index: simulator/tsconfig.json
===================================================================
--- simulator/tsconfig.json prev run
+++ simulator/tsconfig.json this run
@@ -2,8 +2,9 @@
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
+ "types": ["node"],
"strict": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"forceConsistentCasingInFileNames": true,
Index: testcases/emulator_verified/ahmose_gordon_fresh_20260925/ahmose_gordon_75i300l_vs_wuming_225i75l75m.json
===================================================================
--- testcases/emulator_verified/ahmose_gordon_fresh_20260925/ahmose_gordon_75i300l_vs_wuming_225i75l75m.json prev run
+++ testcases/emulator_verified/ahmose_gordon_fresh_20260925/ahmose_gordon_75i300l_vs_wuming_225i75l75m.json this run
@@ -0,0 +1,189 @@
+[
+ {
+ "test_id": "ahmose_gordon_75i300l_vs_wuming_225i75l75m",
+ "description": "C2 battle report 2734693171367029",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 75,
+ "lancer_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 488.62,
+ "defense": 492.87,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 283.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Wu Ming": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 225,
+ "lancer_t6": 75,
+ "marksman_t6": 75
+ },
+ "stats": {
+ "infantry": {
+ "attack": 453.71,
+ "defense": 446.26,
+ "lethality": 223.57,
+ "health": 274.97
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T22:15:42.000Z",
+ "randomseed": 1790374543,
+ "attacker": 35,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171367029",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T22-15-59.133Z-248ee93c-918e-4414-b7c0-4bc53c36380d.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 375,
+ "survivors": 35,
+ "losses": 0,
+ "injured": 120,
+ "lightly_injured": 220,
+ "kills": 133
+ },
+ "defender": {
+ "troops": 375,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 133,
+ "lightly_injured": 242,
+ "kills": 120
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 75,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 27,
+ "lightly_injured": 48,
+ "kills": 5
+ },
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 35,
+ "losses": 0,
+ "injured": 93,
+ "lightly_injured": 172,
+ "kills": 128
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 225,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 79,
+ "lightly_injured": 146,
+ "kills": 25
+ },
+ "lancer_t6": {
+ "troops": 75,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 27,
+ "lightly_injured": 48,
+ "kills": 38
+ },
+ "marksman_t6": {
+ "troops": 75,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 27,
+ "lightly_injured": 48,
+ "kills": 57
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 5,
+ "displayed_kills": null
+ }
+ },
+ "Gordon": {
+ "Venom Infusion": {
+ "triggered": 34,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 22,
+ "displayed_kills": null
+ },
+ "Toxic Release": {
+ "triggered": 17,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_150l_vs_wuming_60i20l20m.json
===================================================================
--- testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_150l_vs_wuming_60i20l20m.json prev run
+++ testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_150l_vs_wuming_60i20l20m.json this run
@@ -0,0 +1,168 @@
+[
+ {
+ "test_id": "gordon_150l_vs_wuming_60i20l20m",
+ "description": "C2 battle report 2734693171367082",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 150
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 488.62,
+ "defense": 492.87,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 283.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Wu Ming": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 60,
+ "lancer_t6": 20,
+ "marksman_t6": 20
+ },
+ "stats": {
+ "infantry": {
+ "attack": 453.71,
+ "defense": 446.26,
+ "lethality": 223.57,
+ "health": 274.97
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T22:22:10.000Z",
+ "randomseed": 1790374931,
+ "attacker": 38,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171367082",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T22-22-27.137Z-0438a9a8-3944-4192-acf7-3bd12fa8eee4.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 150,
+ "survivors": 38,
+ "losses": 0,
+ "injured": 40,
+ "lightly_injured": 72,
+ "kills": 35
+ },
+ "defender": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 40
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 38,
+ "losses": 0,
+ "injured": 40,
+ "lightly_injured": 72,
+ "kills": 35
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 60,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 21,
+ "lightly_injured": 39,
+ "kills": 10
+ },
+ "lancer_t6": {
+ "troops": 20,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 7,
+ "lightly_injured": 13,
+ "kills": 13
+ },
+ "marksman_t6": {
+ "troops": 20,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 7,
+ "lightly_injured": 13,
+ "kills": 17
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 14,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 29,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 19,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_300l_vs_1000i.json
===================================================================
--- testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_300l_vs_1000i.json prev run
+++ testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_300l_vs_1000i.json this run
@@ -0,0 +1,143 @@
+[
+ {
+ "test_id": "gordon_300l_vs_1000i",
+ "description": "C2 battle report 2734693171367011",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 488.62,
+ "defense": 492.87,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 283.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T22:11:08.000Z",
+ "randomseed": 1790374269,
+ "attacker": 0,
+ "defender": 264,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171367011",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T22-11-25.132Z-d4928481-407f-42db-85dd-b73090d96efd.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 258
+ },
+ "defender": {
+ "troops": 1000,
+ "survivors": 264,
+ "losses": 0,
+ "injured": 258,
+ "lightly_injured": 478,
+ "kills": 105
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 258
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 264,
+ "losses": 0,
+ "injured": 258,
+ "lightly_injured": 478,
+ "kills": 105
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 35,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 70,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 46,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_300l_vs_1200m.json
===================================================================
--- testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_300l_vs_1200m.json prev run
+++ testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_300l_vs_1200m.json this run
@@ -0,0 +1,143 @@
+[
+ {
+ "test_id": "gordon_300l_vs_1200m",
+ "description": "C2 battle report 2734693171367016",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 488.62,
+ "defense": 492.87,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 283.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "marksman_t6": 1200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T22:12:16.000Z",
+ "randomseed": 1790374337,
+ "attacker": 0,
+ "defender": 69,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171367016",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T22-12-33.126Z-6fc5dfc9-6262-4f44-9fb0-f65ff578b024.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 396
+ },
+ "defender": {
+ "troops": 1200,
+ "survivors": 69,
+ "losses": 0,
+ "injured": 396,
+ "lightly_injured": 735,
+ "kills": 105
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 396
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 1200,
+ "survivors": 69,
+ "losses": 0,
+ "injured": 396,
+ "lightly_injured": 735,
+ "kills": 105
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 12,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 24,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 16,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_5000l_vs_100l.json
===================================================================
--- testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_5000l_vs_100l.json prev run
+++ testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_5000l_vs_100l.json this run
@@ -0,0 +1,143 @@
+[
+ {
+ "test_id": "gordon_5000l_vs_100l",
+ "description": "C2 battle report 2734693171367273",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 5000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 488.62,
+ "defense": 492.87,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 283.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T22:31:22.000Z",
+ "randomseed": 1790375483,
+ "attacker": 4998,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171367273",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T22-31-39.119Z-8a72c006-02b4-45c2-a073-4c9c5a63e6fb.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 5000,
+ "survivors": 4998,
+ "losses": 0,
+ "injured": 1,
+ "lightly_injured": 1,
+ "kills": 35
+ },
+ "defender": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 1
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 5000,
+ "survivors": 4998,
+ "losses": 0,
+ "injured": 1,
+ "lightly_injured": 1,
+ "kills": 35
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 1
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": null,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 1,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 1,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_5000l_vs_300l.json
===================================================================
--- testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_5000l_vs_300l.json prev run
+++ testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_5000l_vs_300l.json this run
@@ -0,0 +1,143 @@
+[
+ {
+ "test_id": "gordon_5000l_vs_300l",
+ "description": "C2 battle report 2734693171367233",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 5000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 488.62,
+ "defense": 492.87,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 283.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T22:29:28.000Z",
+ "randomseed": 1790375369,
+ "attacker": 4991,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171367233",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T22-29-45.119Z-df1567ac-9079-4e43-a43a-1f9157e8824d.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 5000,
+ "survivors": 4991,
+ "losses": 0,
+ "injured": 4,
+ "lightly_injured": 5,
+ "kills": 105
+ },
+ "defender": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 4
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 5000,
+ "survivors": 4991,
+ "losses": 0,
+ "injured": 4,
+ "lightly_injured": 5,
+ "kills": 105
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 4
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": null,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 1,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 1,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_50i200l_vs_wuming_150i50l50m.json
===================================================================
--- testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_50i200l_vs_wuming_150i50l50m.json prev run
+++ testcases/emulator_verified/ahmose_gordon_fresh_20260925/gordon_50i200l_vs_wuming_150i50l50m.json this run
@@ -0,0 +1,177 @@
+[
+ {
+ "test_id": "gordon_50i200l_vs_wuming_150i50l50m",
+ "description": "C2 battle report 2734693171367045",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 50,
+ "lancer_t6": 200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 488.62,
+ "defense": 492.87,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 283.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Wu Ming": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 150,
+ "lancer_t6": 50,
+ "marksman_t6": 50
+ },
+ "stats": {
+ "infantry": {
+ "attack": 453.71,
+ "defense": 446.26,
+ "lethality": 223.57,
+ "health": 274.97
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T22:17:30.000Z",
+ "randomseed": 1790374651,
+ "attacker": 0,
+ "defender": 126,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171367045",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T22-17-47.116Z-9911b10d-705f-4f44-9884-8ddab46a485e.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 44
+ },
+ "defender": {
+ "troops": 250,
+ "survivors": 126,
+ "losses": 0,
+ "injured": 44,
+ "lightly_injured": 80,
+ "kills": 88
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 1
+ },
+ "lancer_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 43
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 150,
+ "survivors": 26,
+ "losses": 0,
+ "injured": 44,
+ "lightly_injured": 80,
+ "kills": 21
+ },
+ "lancer_t6": {
+ "troops": 50,
+ "survivors": 50,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 29
+ },
+ "marksman_t6": {
+ "troops": 50,
+ "survivors": 50,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 38
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 14,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 29,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 19,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/attdef_test_minxxx_attacks_nc.json
===================================================================
--- testcases/emulator_verified/attdef_test_minxxx_attacks_nc.json prev run
+++ testcases/emulator_verified/attdef_test_minxxx_attacks_nc.json this run
@@ -64,9 +64,9 @@
"health": 28.8
},
"mark": {
"attack": 83.3,
- "defense": 0.0,
+ "defense": 69.9,
"lethality": 37.6,
"health": 29.4
}
},
Index: testcases/emulator_verified/extra_damage_source_death_20260926/calibration_gwen.json
===================================================================
--- testcases/emulator_verified/extra_damage_source_death_20260926/calibration_gwen.json prev run
+++ testcases/emulator_verified/extra_damage_source_death_20260926/calibration_gwen.json this run
@@ -0,0 +1,175 @@
+[
+ {
+ "test_id": "calibration_gwen",
+ "description": "C2 battle report 2734693171388824",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 1000,
+ "marksman_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 281.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 434.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Hendrik": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 1000,
+ "marksman_t6": 500
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 479,
+ "defense": 467.8,
+ "lethality": 244.2,
+ "health": 221.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T11:07:13.000Z",
+ "randomseed": 1790420834,
+ "attacker": 0,
+ "defender": 1191,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171388824",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T11-07-31.130Z-530733f4-c708-48a6-a140-bbe22964fa98.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 385,
+ "lightly_injured": 715,
+ "kills": 109
+ },
+ "defender": {
+ "troops": 1500,
+ "survivors": 1191,
+ "losses": 0,
+ "injured": 109,
+ "lightly_injured": 200,
+ "kills": 385
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 54
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 55
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 696,
+ "losses": 0,
+ "injured": 107,
+ "lightly_injured": 197,
+ "kills": 42
+ },
+ "marksman_t6": {
+ "troops": 500,
+ "survivors": 495,
+ "losses": 0,
+ "injured": 2,
+ "lightly_injured": 3,
+ "kills": 343
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 11,
+ "displayed_kills": 3
+ },
+ "Blastmaster": {
+ "triggered": 4,
+ "displayed_kills": 2
+ }
+ }
+ },
+ "defender": {
+ "Hendrik": {
+ "Dagon's Heir": {
+ "triggered": 14,
+ "displayed_kills": 57
+ },
+ "Armor of Barnacles": {
+ "triggered": 11,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/extra_damage_source_death_20260926/calibration_renee.json
===================================================================
--- testcases/emulator_verified/extra_damage_source_death_20260926/calibration_renee.json prev run
+++ testcases/emulator_verified/extra_damage_source_death_20260926/calibration_renee.json this run
@@ -0,0 +1,144 @@
+[
+ {
+ "test_id": "calibration_renee",
+ "description": "C2 battle report 2734693171388819",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 300,
+ "marksman_t6": 500
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 607.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 283.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T11:06:53.000Z",
+ "randomseed": 1790420814,
+ "attacker": 728,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171388819",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T11-07-10.146Z-92bec361-0ca1-42f2-9729-7dca3a7eadb9.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 800,
+ "survivors": 728,
+ "losses": 0,
+ "injured": 26,
+ "lightly_injured": 46,
+ "kills": 350
+ },
+ "defender": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 26
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 228,
+ "losses": 0,
+ "injured": 26,
+ "lightly_injured": 46,
+ "kills": 197
+ },
+ "marksman_t6": {
+ "troops": 500,
+ "survivors": 500,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 153
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 26
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 12,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/extra_damage_source_death_20260926/calibration_renee_gwen.json
===================================================================
--- testcases/emulator_verified/extra_damage_source_death_20260926/calibration_renee_gwen.json prev run
+++ testcases/emulator_verified/extra_damage_source_death_20260926/calibration_renee_gwen.json this run
@@ -0,0 +1,160 @@
+[
+ {
+ "test_id": "calibration_renee_gwen",
+ "description": "C2 battle report 2734693171388861",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 300,
+ "marksman_t6": 500
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 607.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 434.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T11:09:12.000Z",
+ "randomseed": 1790420953,
+ "attacker": 743,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171388861",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T11-09-29.132Z-69c707d7-413f-41c5-9d0e-88030045e277.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 800,
+ "survivors": 743,
+ "losses": 0,
+ "injured": 20,
+ "lightly_injured": 37,
+ "kills": 350
+ },
+ "defender": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 20
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 243,
+ "losses": 0,
+ "injured": 20,
+ "lightly_injured": 37,
+ "kills": 159
+ },
+ "marksman_t6": {
+ "troops": 500,
+ "survivors": 500,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 191
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 20
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 6,
+ "displayed_kills": 6
+ },
+ "Blastmaster": {
+ "triggered": 3,
+ "displayed_kills": 2
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 9,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/extra_damage_source_death_20260926/gwen_r9_cancellation_control.json
===================================================================
--- testcases/emulator_verified/extra_damage_source_death_20260926/gwen_r9_cancellation_control.json prev run
+++ testcases/emulator_verified/extra_damage_source_death_20260926/gwen_r9_cancellation_control.json this run
@@ -0,0 +1,269 @@
+[
+ {
+ "test_id": "gwen_r9_cancellation_control",
+ "description": "C2 battle report 2734693171408557",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 3000,
+ "marksman_t6": 53
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 281.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 434.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Hendrik": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 500,
+ "marksman_t6": 750
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 479,
+ "defense": 467.8,
+ "lethality": 244.2,
+ "health": 221.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T18:04:00.000Z",
+ "randomseed": 1790445841,
+ "attacker": 194,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171408557",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T18-04-17.244Z-8d12ef4c-be11-4b3b-b3c7-d530ced5dbb1.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 3053,
+ "survivors": 194,
+ "losses": 0,
+ "injured": 1002,
+ "lightly_injured": 1857,
+ "kills": 438
+ },
+ "defender": {
+ "troops": 1250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 438,
+ "lightly_injured": 812,
+ "kills": 1002
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 3000,
+ "survivors": 194,
+ "losses": 0,
+ "injured": 983,
+ "lightly_injured": 1823,
+ "kills": 419
+ },
+ "marksman_t6": {
+ "troops": 53,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 19,
+ "lightly_injured": 34,
+ "kills": 19
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 28
+ },
+ "marksman_t6": {
+ "troops": 750,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 263,
+ "lightly_injured": 487,
+ "kills": 974
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 24,
+ "displayed_kills": 12
+ },
+ "Blastmaster": {
+ "triggered": 2,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Hendrik": {
+ "Dagon's Heir": {
+ "triggered": 45,
+ "displayed_kills": 88
+ },
+ "Armor of Barnacles": {
+ "triggered": 33,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-26T18:04:45.000Z",
+ "randomseed": 1790445886,
+ "attacker": 194,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171408565",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T18-05-02.175Z-452ff48a-6dcc-40ca-8b00-9a5c36a98d5e.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 3053,
+ "survivors": 194,
+ "losses": 0,
+ "injured": 1002,
+ "lightly_injured": 1857,
+ "kills": 438
+ },
+ "defender": {
+ "troops": 1250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 438,
+ "lightly_injured": 812,
+ "kills": 1002
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 3000,
+ "survivors": 194,
+ "losses": 0,
+ "injured": 983,
+ "lightly_injured": 1823,
+ "kills": 419
+ },
+ "marksman_t6": {
+ "troops": 53,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 19,
+ "lightly_injured": 34,
+ "kills": 19
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 28
+ },
+ "marksman_t6": {
+ "troops": 750,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 263,
+ "lightly_injured": 487,
+ "kills": 974
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 24,
+ "displayed_kills": 12
+ },
+ "Blastmaster": {
+ "triggered": 2,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Hendrik": {
+ "Dagon's Heir": {
+ "triggered": 45,
+ "displayed_kills": 88
+ },
+ "Armor of Barnacles": {
+ "triggered": 33,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/extra_damage_source_death_20260926/gwen_r9_cancellation_death.json
===================================================================
--- testcases/emulator_verified/extra_damage_source_death_20260926/gwen_r9_cancellation_death.json prev run
+++ testcases/emulator_verified/extra_damage_source_death_20260926/gwen_r9_cancellation_death.json this run
@@ -0,0 +1,269 @@
+[
+ {
+ "test_id": "gwen_r9_cancellation_death",
+ "description": "C2 battle report 2734693171408434",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 3000,
+ "marksman_t6": 52
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 281.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 434.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Hendrik": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 500,
+ "marksman_t6": 750
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 479,
+ "defense": 467.8,
+ "lethality": 244.2,
+ "health": 221.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T17:57:27.000Z",
+ "randomseed": 1790445448,
+ "attacker": 192,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171408434",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T17-57-45.229Z-2d7a73d1-1248-4d76-9a3a-0c69c963d44e.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 3052,
+ "survivors": 192,
+ "losses": 0,
+ "injured": 1002,
+ "lightly_injured": 1858,
+ "kills": 438
+ },
+ "defender": {
+ "troops": 1250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 438,
+ "lightly_injured": 812,
+ "kills": 1002
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 3000,
+ "survivors": 192,
+ "losses": 0,
+ "injured": 983,
+ "lightly_injured": 1825,
+ "kills": 420
+ },
+ "marksman_t6": {
+ "troops": 52,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 19,
+ "lightly_injured": 33,
+ "kills": 18
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 29
+ },
+ "marksman_t6": {
+ "troops": 750,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 263,
+ "lightly_injured": 487,
+ "kills": 973
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 23,
+ "displayed_kills": 15
+ },
+ "Blastmaster": {
+ "triggered": 1,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Hendrik": {
+ "Dagon's Heir": {
+ "triggered": 45,
+ "displayed_kills": 87
+ },
+ "Armor of Barnacles": {
+ "triggered": 33,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-26T17:58:13.000Z",
+ "randomseed": 1790445494,
+ "attacker": 192,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171408443",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T17-58-30.168Z-b0c109cf-6ebe-4725-a945-18cbeaf908ad.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 3052,
+ "survivors": 192,
+ "losses": 0,
+ "injured": 1002,
+ "lightly_injured": 1858,
+ "kills": 438
+ },
+ "defender": {
+ "troops": 1250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 438,
+ "lightly_injured": 812,
+ "kills": 1002
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 3000,
+ "survivors": 192,
+ "losses": 0,
+ "injured": 983,
+ "lightly_injured": 1825,
+ "kills": 420
+ },
+ "marksman_t6": {
+ "troops": 52,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 19,
+ "lightly_injured": 33,
+ "kills": 18
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 29
+ },
+ "marksman_t6": {
+ "troops": 750,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 263,
+ "lightly_injured": 487,
+ "kills": 973
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 23,
+ "displayed_kills": 15
+ },
+ "Blastmaster": {
+ "triggered": 1,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Hendrik": {
+ "Dagon's Heir": {
+ "triggered": 45,
+ "displayed_kills": 87
+ },
+ "Armor of Barnacles": {
+ "triggered": 33,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/extra_damage_source_death_20260926/gwen_r9_delayed_control.json
===================================================================
--- testcases/emulator_verified/extra_damage_source_death_20260926/gwen_r9_delayed_control.json prev run
+++ testcases/emulator_verified/extra_damage_source_death_20260926/gwen_r9_delayed_control.json this run
@@ -0,0 +1,269 @@
+[
+ {
+ "test_id": "gwen_r9_delayed_control",
+ "description": "C2 battle report 2734693171408658",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 3000,
+ "marksman_t6": 31
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 281.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 434.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Hendrik": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 1000,
+ "marksman_t6": 250
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 479,
+ "defense": 467.8,
+ "lethality": 244.2,
+ "health": 221.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T18:11:36.000Z",
+ "randomseed": 1790446297,
+ "attacker": 369,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171408658",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T18-11-53.186Z-1b72cd00-24b4-4424-a041-5a8836f0c048.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 3031,
+ "survivors": 369,
+ "losses": 0,
+ "injured": 932,
+ "lightly_injured": 1730,
+ "kills": 438
+ },
+ "defender": {
+ "troops": 1250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 438,
+ "lightly_injured": 812,
+ "kills": 932
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 3000,
+ "survivors": 369,
+ "losses": 0,
+ "injured": 921,
+ "lightly_injured": 1710,
+ "kills": 423
+ },
+ "marksman_t6": {
+ "troops": 31,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 11,
+ "lightly_injured": 20,
+ "kills": 15
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 98
+ },
+ "marksman_t6": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 834
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 29,
+ "displayed_kills": 16
+ },
+ "Blastmaster": {
+ "triggered": 2,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Hendrik": {
+ "Dagon's Heir": {
+ "triggered": 55,
+ "displayed_kills": 70
+ },
+ "Armor of Barnacles": {
+ "triggered": 41,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-26T18:12:21.000Z",
+ "randomseed": 1790446342,
+ "attacker": 369,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171408672",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T18-12-38.205Z-ac7cba85-d246-44e2-beb3-fa15c0941d5c.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 3031,
+ "survivors": 369,
+ "losses": 0,
+ "injured": 932,
+ "lightly_injured": 1730,
+ "kills": 438
+ },
+ "defender": {
+ "troops": 1250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 438,
+ "lightly_injured": 812,
+ "kills": 932
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 3000,
+ "survivors": 369,
+ "losses": 0,
+ "injured": 921,
+ "lightly_injured": 1710,
+ "kills": 423
+ },
+ "marksman_t6": {
+ "troops": 31,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 11,
+ "lightly_injured": 20,
+ "kills": 15
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 98
+ },
+ "marksman_t6": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 834
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 29,
+ "displayed_kills": 16
+ },
+ "Blastmaster": {
+ "triggered": 2,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Hendrik": {
+ "Dagon's Heir": {
+ "triggered": 55,
+ "displayed_kills": 70
+ },
+ "Armor of Barnacles": {
+ "triggered": 41,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/extra_damage_source_death_20260926/gwen_r9_placement_control.json
===================================================================
--- testcases/emulator_verified/extra_damage_source_death_20260926/gwen_r9_placement_control.json prev run
+++ testcases/emulator_verified/extra_damage_source_death_20260926/gwen_r9_placement_control.json this run
@@ -0,0 +1,269 @@
+[
+ {
+ "test_id": "gwen_r9_placement_control",
+ "description": "C2 battle report 2734693171408610",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 2000,
+ "marksman_t6": 24
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 281.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 434.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Hendrik": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 500,
+ "marksman_t6": 250
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 479,
+ "defense": 467.8,
+ "lethality": 244.2,
+ "health": 221.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T18:07:26.000Z",
+ "randomseed": 1790446047,
+ "attacker": 453,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171408610",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T18-07-43.221Z-5a0ad508-a114-4f33-8624-e0b1802ffb75.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 2024,
+ "survivors": 453,
+ "losses": 0,
+ "injured": 551,
+ "lightly_injured": 1020,
+ "kills": 263
+ },
+ "defender": {
+ "troops": 750,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 263,
+ "lightly_injured": 487,
+ "kills": 551
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 2000,
+ "survivors": 453,
+ "losses": 0,
+ "injured": 542,
+ "lightly_injured": 1005,
+ "kills": 253
+ },
+ "marksman_t6": {
+ "troops": 24,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 9,
+ "lightly_injured": 15,
+ "kills": 10
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 37
+ },
+ "marksman_t6": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 514
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 24,
+ "displayed_kills": 8
+ },
+ "Blastmaster": {
+ "triggered": 2,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Hendrik": {
+ "Dagon's Heir": {
+ "triggered": 44,
+ "displayed_kills": 47
+ },
+ "Armor of Barnacles": {
+ "triggered": 33,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-26T18:08:11.000Z",
+ "randomseed": 1790446092,
+ "attacker": 453,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171408615",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T18-08-28.174Z-46a5e4b8-cee9-4395-bd6b-24f3eff69e20.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 2024,
+ "survivors": 453,
+ "losses": 0,
+ "injured": 551,
+ "lightly_injured": 1020,
+ "kills": 263
+ },
+ "defender": {
+ "troops": 750,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 263,
+ "lightly_injured": 487,
+ "kills": 551
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 2000,
+ "survivors": 453,
+ "losses": 0,
+ "injured": 542,
+ "lightly_injured": 1005,
+ "kills": 253
+ },
+ "marksman_t6": {
+ "troops": 24,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 9,
+ "lightly_injured": 15,
+ "kills": 10
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 37
+ },
+ "marksman_t6": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 514
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 24,
+ "displayed_kills": 8
+ },
+ "Blastmaster": {
+ "triggered": 2,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Hendrik": {
+ "Dagon's Heir": {
+ "triggered": 44,
+ "displayed_kills": 47
+ },
+ "Armor of Barnacles": {
+ "triggered": 33,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/extra_damage_source_death_20260926/gwen_r9_placement_death.json
===================================================================
--- testcases/emulator_verified/extra_damage_source_death_20260926/gwen_r9_placement_death.json prev run
+++ testcases/emulator_verified/extra_damage_source_death_20260926/gwen_r9_placement_death.json this run
@@ -0,0 +1,269 @@
+[
+ {
+ "test_id": "gwen_r9_placement_death",
+ "description": "C2 battle report 2734693171408572",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 2000,
+ "marksman_t6": 23
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 281.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 434.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Hendrik": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 500,
+ "marksman_t6": 250
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 479,
+ "defense": 467.8,
+ "lethality": 244.2,
+ "health": 221.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T18:05:30.000Z",
+ "randomseed": 1790445931,
+ "attacker": 436,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171408572",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T18-05-47.242Z-81786722-7074-4ce9-8629-d810983a753a.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 2023,
+ "survivors": 436,
+ "losses": 0,
+ "injured": 557,
+ "lightly_injured": 1030,
+ "kills": 263
+ },
+ "defender": {
+ "troops": 750,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 263,
+ "lightly_injured": 487,
+ "kills": 557
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 2000,
+ "survivors": 436,
+ "losses": 0,
+ "injured": 548,
+ "lightly_injured": 1016,
+ "kills": 253
+ },
+ "marksman_t6": {
+ "troops": 23,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 9,
+ "lightly_injured": 14,
+ "kills": 10
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 39
+ },
+ "marksman_t6": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 518
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 23,
+ "displayed_kills": 8
+ },
+ "Blastmaster": {
+ "triggered": 1,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Hendrik": {
+ "Dagon's Heir": {
+ "triggered": 45,
+ "displayed_kills": 44
+ },
+ "Armor of Barnacles": {
+ "triggered": 33,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-26T18:06:15.000Z",
+ "randomseed": 1790445976,
+ "attacker": 436,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171408590",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T18-06-32.177Z-b729f1ef-3778-4b31-9e3a-dd3abaa17e8a.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 2023,
+ "survivors": 436,
+ "losses": 0,
+ "injured": 557,
+ "lightly_injured": 1030,
+ "kills": 263
+ },
+ "defender": {
+ "troops": 750,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 263,
+ "lightly_injured": 487,
+ "kills": 557
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 2000,
+ "survivors": 436,
+ "losses": 0,
+ "injured": 548,
+ "lightly_injured": 1016,
+ "kills": 253
+ },
+ "marksman_t6": {
+ "troops": 23,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 9,
+ "lightly_injured": 14,
+ "kills": 10
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 39
+ },
+ "marksman_t6": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 518
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 23,
+ "displayed_kills": 8
+ },
+ "Blastmaster": {
+ "triggered": 1,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Hendrik": {
+ "Dagon's Heir": {
+ "triggered": 45,
+ "displayed_kills": 44
+ },
+ "Armor of Barnacles": {
+ "triggered": 33,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/extra_damage_source_death_20260926/renee_gwen_source_death.json
===================================================================
--- testcases/emulator_verified/extra_damage_source_death_20260926/renee_gwen_source_death.json prev run
+++ testcases/emulator_verified/extra_damage_source_death_20260926/renee_gwen_source_death.json this run
@@ -0,0 +1,241 @@
+[
+ {
+ "test_id": "renee_gwen_source_death",
+ "description": "C2 battle report 2734693171408478",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 79,
+ "marksman_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 607.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 434.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 3000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T18:00:38.000Z",
+ "randomseed": 1790445639,
+ "attacker": 0,
+ "defender": 12,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171408478",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T18-00-55.184Z-1b0aaf6e-b0f8-4058-a813-1b69f2d3c66e.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1079,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 378,
+ "lightly_injured": 701,
+ "kills": 1046
+ },
+ "defender": {
+ "troops": 3000,
+ "survivors": 12,
+ "losses": 0,
+ "injured": 1046,
+ "lightly_injured": 1942,
+ "kills": 378
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 79,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 28,
+ "lightly_injured": 51,
+ "kills": 27
+ },
+ "marksman_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 1019
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 3000,
+ "survivors": 12,
+ "losses": 0,
+ "injured": 1046,
+ "lightly_injured": 1942,
+ "kills": 378
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 27,
+ "displayed_kills": 31
+ },
+ "Blastmaster": {
+ "triggered": 32,
+ "displayed_kills": 19
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 80,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-26T18:01:23.000Z",
+ "randomseed": 1790445684,
+ "attacker": 0,
+ "defender": 12,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171408507",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T18-01-40.204Z-654b3470-11f7-49f1-9488-2f44c690cf4c.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1079,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 378,
+ "lightly_injured": 701,
+ "kills": 1046
+ },
+ "defender": {
+ "troops": 3000,
+ "survivors": 12,
+ "losses": 0,
+ "injured": 1046,
+ "lightly_injured": 1942,
+ "kills": 378
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 79,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 28,
+ "lightly_injured": 51,
+ "kills": 27
+ },
+ "marksman_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 1019
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 3000,
+ "survivors": 12,
+ "losses": 0,
+ "injured": 1046,
+ "lightly_injured": 1942,
+ "kills": 378
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 27,
+ "displayed_kills": 31
+ },
+ "Blastmaster": {
+ "triggered": 32,
+ "displayed_kills": 19
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 80,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/extra_damage_source_death_20260926/renee_gwen_survival_control.json
===================================================================
--- testcases/emulator_verified/extra_damage_source_death_20260926/renee_gwen_survival_control.json prev run
+++ testcases/emulator_verified/extra_damage_source_death_20260926/renee_gwen_survival_control.json this run
@@ -0,0 +1,241 @@
+[
+ {
+ "test_id": "renee_gwen_survival_control",
+ "description": "C2 battle report 2734693171408520",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 84,
+ "marksman_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 607.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 434.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 3000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T18:02:15.000Z",
+ "randomseed": 1790445736,
+ "attacker": 90,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171408520",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T18-02-32.179Z-c2d3a7db-9772-4c67-8d3b-f9c0cb18ab5c.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1084,
+ "survivors": 90,
+ "losses": 0,
+ "injured": 349,
+ "lightly_injured": 645,
+ "kills": 1050
+ },
+ "defender": {
+ "troops": 3000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 1050,
+ "lightly_injured": 1950,
+ "kills": 349
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 84,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 30,
+ "lightly_injured": 54,
+ "kills": 29
+ },
+ "marksman_t6": {
+ "troops": 1000,
+ "survivors": 90,
+ "losses": 0,
+ "injured": 319,
+ "lightly_injured": 591,
+ "kills": 1021
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 3000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 1050,
+ "lightly_injured": 1950,
+ "kills": 349
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 23,
+ "displayed_kills": 30
+ },
+ "Blastmaster": {
+ "triggered": 27,
+ "displayed_kills": 18
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 68,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-26T18:03:00.000Z",
+ "randomseed": 1790445781,
+ "attacker": 90,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171408548",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T18-03-17.244Z-e2676e3e-a9d2-4ef2-83c5-1ed62d12c84a.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1084,
+ "survivors": 90,
+ "losses": 0,
+ "injured": 349,
+ "lightly_injured": 645,
+ "kills": 1050
+ },
+ "defender": {
+ "troops": 3000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 1050,
+ "lightly_injured": 1950,
+ "kills": 349
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 84,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 30,
+ "lightly_injured": 54,
+ "kills": 29
+ },
+ "marksman_t6": {
+ "troops": 1000,
+ "survivors": 90,
+ "losses": 0,
+ "injured": 319,
+ "lightly_injured": 591,
+ "kills": 1021
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 3000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 1050,
+ "lightly_injured": 1950,
+ "kills": 349
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 23,
+ "displayed_kills": 30
+ },
+ "Blastmaster": {
+ "triggered": 27,
+ "displayed_kills": 18
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 68,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/extra_damage_source_death_20260926/renee_source_death.json
===================================================================
--- testcases/emulator_verified/extra_damage_source_death_20260926/renee_source_death.json prev run
+++ testcases/emulator_verified/extra_damage_source_death_20260926/renee_source_death.json this run
@@ -0,0 +1,215 @@
+[
+ {
+ "test_id": "renee_source_death",
+ "description": "C2 battle report 2734693171388980",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 62,
+ "marksman_t6": 800
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 607.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 283.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 1500
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T11:13:33.000Z",
+ "randomseed": 1790421214,
+ "attacker": 10,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171388980",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T11-13-50.165Z-8253a420-20d4-426c-904c-10ab499e9377.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 862,
+ "survivors": 10,
+ "losses": 0,
+ "injured": 299,
+ "lightly_injured": 553,
+ "kills": 525
+ },
+ "defender": {
+ "troops": 1500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 525,
+ "lightly_injured": 975,
+ "kills": 299
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 62,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 22,
+ "lightly_injured": 40,
+ "kills": 25
+ },
+ "marksman_t6": {
+ "troops": 800,
+ "survivors": 10,
+ "losses": 0,
+ "injured": 277,
+ "lightly_injured": 513,
+ "kills": 500
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 525,
+ "lightly_injured": 975,
+ "kills": 299
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 75,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-26T11:14:18.000Z",
+ "randomseed": 1790421259,
+ "attacker": 10,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171389013",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T11-14-35.129Z-d0a853a7-47fb-4ef1-a47a-a500d7761e9d.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 862,
+ "survivors": 10,
+ "losses": 0,
+ "injured": 299,
+ "lightly_injured": 553,
+ "kills": 525
+ },
+ "defender": {
+ "troops": 1500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 525,
+ "lightly_injured": 975,
+ "kills": 299
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 62,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 22,
+ "lightly_injured": 40,
+ "kills": 25
+ },
+ "marksman_t6": {
+ "troops": 800,
+ "survivors": 10,
+ "losses": 0,
+ "injured": 277,
+ "lightly_injured": 513,
+ "kills": 500
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 525,
+ "lightly_injured": 975,
+ "kills": 299
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 75,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/extra_damage_source_death_20260926/renee_survival_control.json
===================================================================
--- testcases/emulator_verified/extra_damage_source_death_20260926/renee_survival_control.json prev run
+++ testcases/emulator_verified/extra_damage_source_death_20260926/renee_survival_control.json this run
@@ -0,0 +1,215 @@
+[
+ {
+ "test_id": "renee_survival_control",
+ "description": "C2 battle report 2734693171408415",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 63,
+ "marksman_t6": 800
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 607.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 283.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 1500
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T17:56:15.000Z",
+ "randomseed": 1790445376,
+ "attacker": 69,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171408415",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T17-56-32.185Z-2cdd3111-ec54-4bd3-bf41-0526b5b4e05a.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 863,
+ "survivors": 69,
+ "losses": 0,
+ "injured": 279,
+ "lightly_injured": 515,
+ "kills": 525
+ },
+ "defender": {
+ "troops": 1500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 525,
+ "lightly_injured": 975,
+ "kills": 279
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 63,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 23,
+ "lightly_injured": 40,
+ "kills": 27
+ },
+ "marksman_t6": {
+ "troops": 800,
+ "survivors": 69,
+ "losses": 0,
+ "injured": 256,
+ "lightly_injured": 475,
+ "kills": 498
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 525,
+ "lightly_injured": 975,
+ "kills": 279
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 66,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-26T17:57:00.000Z",
+ "randomseed": 1790445421,
+ "attacker": 69,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171408426",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T17-57-17.242Z-9d99cbcc-5df0-4e76-adfe-0c3e6ae3120f.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 863,
+ "survivors": 69,
+ "losses": 0,
+ "injured": 279,
+ "lightly_injured": 515,
+ "kills": 525
+ },
+ "defender": {
+ "troops": 1500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 525,
+ "lightly_injured": 975,
+ "kills": 279
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 63,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 23,
+ "lightly_injured": 40,
+ "kills": 27
+ },
+ "marksman_t6": {
+ "troops": 800,
+ "survivors": 69,
+ "losses": 0,
+ "injured": 256,
+ "lightly_injured": 475,
+ "kills": 498
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 525,
+ "lightly_injured": 975,
+ "kills": 279
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 66,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gatot_fresh_20260925/gatot_mixed_gordon_gwen.json
===================================================================
--- testcases/emulator_verified/gatot_fresh_20260925/gatot_mixed_gordon_gwen.json prev run
+++ testcases/emulator_verified/gatot_fresh_20260925/gatot_mixed_gordon_gwen.json this run
@@ -0,0 +1,235 @@
+[
+ {
+ "test_id": "gatot_mixed_gordon_gwen",
+ "description": "C2 battle report 2734693171351221",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Wu Ming": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 2,
+ "skill_4": 0
+ },
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 315,
+ "lancer_t6": 353,
+ "marksman_t6": 204
+ },
+ "stats": {
+ "infantry": {
+ "attack": 513.98,
+ "defense": 496.98,
+ "lethality": 250.71,
+ "health": 274.79
+ },
+ "lancer": {
+ "attack": 493.62,
+ "defense": 487.87,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 435.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gatot": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Jessie": {
+ "skill_1": 1,
+ "skill_2": 1
+ },
+ "Hendrik": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 270,
+ "lancer_t6": 350,
+ "marksman_t6": 243
+ },
+ "stats": {
+ "infantry": {
+ "attack": 627.31,
+ "defense": 619.86,
+ "lethality": 230.66,
+ "health": 281.02
+ },
+ "lancer": {
+ "attack": 297.72,
+ "defense": 297.02,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 479,
+ "defense": 467.8,
+ "lethality": 244.2,
+ "health": 221.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T13:25:56.000Z",
+ "randomseed": 1790342757,
+ "attacker": 0,
+ "defender": 593,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171351221",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T13-26-12.142Z-057d5450-6a59-42f8-b6ed-5f8d74d49fad.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 872,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 307,
+ "lightly_injured": 565,
+ "kills": 96
+ },
+ "defender": {
+ "troops": 863,
+ "survivors": 593,
+ "losses": 0,
+ "injured": 96,
+ "lightly_injured": 174,
+ "kills": 307
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 315,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 111,
+ "lightly_injured": 204,
+ "kills": 0
+ },
+ "lancer_t6": {
+ "troops": 353,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 124,
+ "lightly_injured": 229,
+ "kills": 36
+ },
+ "marksman_t6": {
+ "troops": 204,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 72,
+ "lightly_injured": 132,
+ "kills": 60
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 270,
+ "survivors": 33,
+ "losses": 0,
+ "injured": 83,
+ "lightly_injured": 154,
+ "kills": 28
+ },
+ "lancer_t6": {
+ "troops": 350,
+ "survivors": 332,
+ "losses": 0,
+ "injured": 7,
+ "lightly_injured": 11,
+ "kills": 78
+ },
+ "marksman_t6": {
+ "troops": 243,
+ "survivors": 228,
+ "losses": 0,
+ "injured": 6,
+ "lightly_injured": 9,
+ "kills": 201
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 18,
+ "displayed_kills": 3
+ },
+ "Blastmaster": {
+ "triggered": 8,
+ "displayed_kills": 12
+ }
+ },
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 10,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 20,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 14,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Hendrik": {
+ "Dagon's Heir": {
+ "triggered": 14,
+ "displayed_kills": 61
+ },
+ "Armor of Barnacles": {
+ "triggered": 10,
+ "displayed_kills": null
+ }
+ },
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": 43,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gatot_fresh_20260925/gatot_mixed_without_gordon.json
===================================================================
--- testcases/emulator_verified/gatot_fresh_20260925/gatot_mixed_without_gordon.json prev run
+++ testcases/emulator_verified/gatot_fresh_20260925/gatot_mixed_without_gordon.json this run
@@ -0,0 +1,215 @@
+[
+ {
+ "test_id": "gatot_mixed_without_gordon",
+ "description": "C2 battle report 2734693171351259",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Wu Ming": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 2,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 315,
+ "lancer_t6": 353,
+ "marksman_t6": 204
+ },
+ "stats": {
+ "infantry": {
+ "attack": 513.98,
+ "defense": 496.98,
+ "lethality": 250.71,
+ "health": 274.79
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 435.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gatot": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Jessie": {
+ "skill_1": 1,
+ "skill_2": 1
+ },
+ "Hendrik": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 270,
+ "lancer_t6": 350,
+ "marksman_t6": 243
+ },
+ "stats": {
+ "infantry": {
+ "attack": 627.31,
+ "defense": 619.86,
+ "lethality": 230.66,
+ "health": 281.02
+ },
+ "lancer": {
+ "attack": 297.72,
+ "defense": 297.02,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 479,
+ "defense": 467.8,
+ "lethality": 244.2,
+ "health": 221.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T13:27:28.000Z",
+ "randomseed": 1790342849,
+ "attacker": 0,
+ "defender": 726,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171351259",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T13-27-44.130Z-a2aec42e-5a43-4aee-b690-4d22d33ceb83.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 872,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 307,
+ "lightly_injured": 565,
+ "kills": 49
+ },
+ "defender": {
+ "troops": 863,
+ "survivors": 726,
+ "losses": 0,
+ "injured": 49,
+ "lightly_injured": 88,
+ "kills": 307
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 315,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 111,
+ "lightly_injured": 204,
+ "kills": 0
+ },
+ "lancer_t6": {
+ "troops": 353,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 124,
+ "lightly_injured": 229,
+ "kills": 6
+ },
+ "marksman_t6": {
+ "troops": 204,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 72,
+ "lightly_injured": 132,
+ "kills": 43
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 270,
+ "survivors": 162,
+ "losses": 0,
+ "injured": 38,
+ "lightly_injured": 70,
+ "kills": 37
+ },
+ "lancer_t6": {
+ "troops": 350,
+ "survivors": 334,
+ "losses": 0,
+ "injured": 6,
+ "lightly_injured": 10,
+ "kills": 69
+ },
+ "marksman_t6": {
+ "troops": 243,
+ "survivors": 230,
+ "losses": 0,
+ "injured": 5,
+ "lightly_injured": 8,
+ "kills": 201
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 15,
+ "displayed_kills": 1
+ },
+ "Blastmaster": {
+ "triggered": 7,
+ "displayed_kills": 11
+ }
+ }
+ },
+ "defender": {
+ "Hendrik": {
+ "Dagon's Heir": {
+ "triggered": 12,
+ "displayed_kills": 71
+ },
+ "Armor of Barnacles": {
+ "triggered": 9,
+ "displayed_kills": null
+ }
+ },
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": 36,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gatot_fresh_20260925/gatot_mixed_without_gwen.json
===================================================================
--- testcases/emulator_verified/gatot_fresh_20260925/gatot_mixed_without_gwen.json prev run
+++ testcases/emulator_verified/gatot_fresh_20260925/gatot_mixed_without_gwen.json this run
@@ -0,0 +1,219 @@
+[
+ {
+ "test_id": "gatot_mixed_without_gwen",
+ "description": "C2 battle report 2734693171351287",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Wu Ming": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 2,
+ "skill_4": 0
+ },
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 315,
+ "lancer_t6": 353,
+ "marksman_t6": 204
+ },
+ "stats": {
+ "infantry": {
+ "attack": 513.98,
+ "defense": 496.98,
+ "lethality": 250.71,
+ "health": 274.79
+ },
+ "lancer": {
+ "attack": 493.62,
+ "defense": 487.87,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gatot": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Jessie": {
+ "skill_1": 1,
+ "skill_2": 1
+ },
+ "Hendrik": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 270,
+ "lancer_t6": 350,
+ "marksman_t6": 243
+ },
+ "stats": {
+ "infantry": {
+ "attack": 627.31,
+ "defense": 619.86,
+ "lethality": 230.66,
+ "health": 281.02
+ },
+ "lancer": {
+ "attack": 297.72,
+ "defense": 297.02,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 479,
+ "defense": 467.8,
+ "lethality": 244.2,
+ "health": 221.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T13:29:36.000Z",
+ "randomseed": 1790342977,
+ "attacker": 0,
+ "defender": 722,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171351287",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T13-29-52.137Z-fc0b1017-6ca0-4480-bb39-39c494903c90.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 872,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 307,
+ "lightly_injured": 565,
+ "kills": 50
+ },
+ "defender": {
+ "troops": 863,
+ "survivors": 722,
+ "losses": 0,
+ "injured": 50,
+ "lightly_injured": 91,
+ "kills": 307
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 315,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 111,
+ "lightly_injured": 204,
+ "kills": 0
+ },
+ "lancer_t6": {
+ "troops": 353,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 124,
+ "lightly_injured": 229,
+ "kills": 22
+ },
+ "marksman_t6": {
+ "troops": 204,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 72,
+ "lightly_injured": 132,
+ "kills": 28
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 270,
+ "survivors": 129,
+ "losses": 0,
+ "injured": 50,
+ "lightly_injured": 91,
+ "kills": 33
+ },
+ "lancer_t6": {
+ "troops": 350,
+ "survivors": 350,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 70
+ },
+ "marksman_t6": {
+ "troops": 243,
+ "survivors": 243,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 204
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 9,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 19,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 13,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Hendrik": {
+ "Dagon's Heir": {
+ "triggered": 13,
+ "displayed_kills": 71
+ },
+ "Armor of Barnacles": {
+ "triggered": 9,
+ "displayed_kills": null
+ }
+ },
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": 39,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gordon_gwen_t9_probe_20260929/gg_1000i_1000l_vs_20000t9.json
===================================================================
--- testcases/emulator_verified/gordon_gwen_t9_probe_20260929/gg_1000i_1000l_vs_20000t9.json prev run
+++ testcases/emulator_verified/gordon_gwen_t9_probe_20260929/gg_1000i_1000l_vs_20000t9.json this run
@@ -0,0 +1,175 @@
+[
+ {
+ "test_id": "gg_1000i_1000l_vs_20000t9",
+ "description": "C2 battle report 2734693171529413",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 1000,
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 493.62,
+ "defense": 487.87,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 435.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t9": 20000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 258.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T13:22:28.000Z",
+ "randomseed": 1790688149,
+ "attacker": 0,
+ "defender": 17781,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171529413",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T13-22-44.129Z-2e389ab6-7179-425b-a340-ca62e18cec4f.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 2000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 700,
+ "lightly_injured": 1300,
+ "kills": 777
+ },
+ "defender": {
+ "troops": 20000,
+ "survivors": 17781,
+ "losses": 0,
+ "injured": 777,
+ "lightly_injured": 1442,
+ "kills": 700
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 77
+ },
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 700
+ }
+ },
+ "defender": {
+ "lancer_t9": {
+ "troops": 20000,
+ "survivors": 17781,
+ "losses": 0,
+ "injured": 777,
+ "lightly_injured": 1442,
+ "kills": 700
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 6,
+ "displayed_kills": 25
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 6,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 12,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 8,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Troop skills": {
+ "Ambusher": {
+ "triggered": 5,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gordon_gwen_t9_probe_20260929/gg_1000l_vs_20000t9.json
===================================================================
--- testcases/emulator_verified/gordon_gwen_t9_probe_20260929/gg_1000l_vs_20000t9.json prev run
+++ testcases/emulator_verified/gordon_gwen_t9_probe_20260929/gg_1000l_vs_20000t9.json this run
@@ -0,0 +1,166 @@
+[
+ {
+ "test_id": "gg_1000l_vs_20000t9",
+ "description": "C2 battle report 2734693171525958",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 493.62,
+ "defense": 487.87,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 435.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t9": 20000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 258.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T10:32:35.000Z",
+ "randomseed": 1790677956,
+ "attacker": 0,
+ "defender": 19522,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171525958",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T10-32-51.116Z-d76be9d4-fc03-4b9e-bd02-43afbb6cde0e.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 168
+ },
+ "defender": {
+ "troops": 20000,
+ "survivors": 19522,
+ "losses": 0,
+ "injured": 168,
+ "lightly_injured": 310,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 168
+ }
+ },
+ "defender": {
+ "lancer_t9": {
+ "troops": 20000,
+ "survivors": 19522,
+ "losses": 0,
+ "injured": 168,
+ "lightly_injured": 310,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 1,
+ "displayed_kills": 3
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 2,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 5,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 3,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Troop skills": {
+ "Ambusher": {
+ "triggered": 2,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gordon_gwen_t9_probe_20260929/gg_1000l_vs_20000t9l_10000t9m.json
===================================================================
--- testcases/emulator_verified/gordon_gwen_t9_probe_20260929/gg_1000l_vs_20000t9l_10000t9m.json prev run
+++ testcases/emulator_verified/gordon_gwen_t9_probe_20260929/gg_1000l_vs_20000t9l_10000t9m.json this run
@@ -0,0 +1,179 @@
+[
+ {
+ "test_id": "gg_1000l_vs_20000t9l_10000t9m",
+ "description": "C2 battle report 2734693171526025",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 493.62,
+ "defense": 487.87,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 435.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t9": 20000,
+ "marksman_t9": 10000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 258.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T10:39:37.000Z",
+ "randomseed": 1790678378,
+ "attacker": 0,
+ "defender": 29802,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171526025",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T10-39-53.124Z-16f3243d-fcfc-4721-b36d-a24f5a7b90d0.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 70
+ },
+ "defender": {
+ "troops": 30000,
+ "survivors": 29802,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 128,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 70
+ }
+ },
+ "defender": {
+ "lancer_t9": {
+ "troops": 20000,
+ "survivors": 19802,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 128,
+ "kills": 168
+ },
+ "marksman_t9": {
+ "troops": 10000,
+ "survivors": 10000,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 182
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": null,
+ "displayed_kills": null
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 1,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 2,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 1,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Troop skills": {
+ "Ambusher": {
+ "triggered": 4,
+ "displayed_kills": null
+ },
+ "Volley": {
+ "triggered": 1,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gordon_gwen_t9_probe_20260929/gg_60i_1000l_vs_20000t9.json
===================================================================
--- testcases/emulator_verified/gordon_gwen_t9_probe_20260929/gg_60i_1000l_vs_20000t9.json prev run
+++ testcases/emulator_verified/gordon_gwen_t9_probe_20260929/gg_60i_1000l_vs_20000t9.json this run
@@ -0,0 +1,175 @@
+[
+ {
+ "test_id": "gg_60i_1000l_vs_20000t9",
+ "description": "C2 battle report 2734693171525976",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 60,
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 493.62,
+ "defense": 487.87,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 435.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t9": 20000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 258.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T10:34:07.000Z",
+ "randomseed": 1790678048,
+ "attacker": 0,
+ "defender": 19352,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171525976",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T10-34-23.127Z-cf5d2472-b91f-43ec-a71a-adc75c5e37db.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1060,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 371,
+ "lightly_injured": 689,
+ "kills": 227
+ },
+ "defender": {
+ "troops": 20000,
+ "survivors": 19352,
+ "losses": 0,
+ "injured": 227,
+ "lightly_injured": 421,
+ "kills": 371
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 60,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 21,
+ "lightly_injured": 39,
+ "kills": 1
+ },
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 226
+ }
+ },
+ "defender": {
+ "lancer_t9": {
+ "troops": 20000,
+ "survivors": 19352,
+ "losses": 0,
+ "injured": 227,
+ "lightly_injured": 421,
+ "kills": 371
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 2,
+ "displayed_kills": 6
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 3,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 6,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 4,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Troop skills": {
+ "Ambusher": {
+ "triggered": 2,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gordon_gwen_t9_probe_20260929/gg_700i_1000l_vs_20000t9.json
===================================================================
--- testcases/emulator_verified/gordon_gwen_t9_probe_20260929/gg_700i_1000l_vs_20000t9.json prev run
+++ testcases/emulator_verified/gordon_gwen_t9_probe_20260929/gg_700i_1000l_vs_20000t9.json this run
@@ -0,0 +1,175 @@
+[
+ {
+ "test_id": "gg_700i_1000l_vs_20000t9",
+ "description": "C2 battle report 2734693171529402",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 700,
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 493.62,
+ "defense": 487.87,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 435.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t9": 20000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 258.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T13:21:42.000Z",
+ "randomseed": 1790688103,
+ "attacker": 0,
+ "defender": 18333,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171529402",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T13-21-58.135Z-bff61946-725a-40ca-b8b5-a8024749d8ac.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1700,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 595,
+ "lightly_injured": 1105,
+ "kills": 584
+ },
+ "defender": {
+ "troops": 20000,
+ "survivors": 18333,
+ "losses": 0,
+ "injured": 584,
+ "lightly_injured": 1083,
+ "kills": 595
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 700,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 245,
+ "lightly_injured": 455,
+ "kills": 43
+ },
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 541
+ }
+ },
+ "defender": {
+ "lancer_t9": {
+ "troops": 20000,
+ "survivors": 18333,
+ "losses": 0,
+ "injured": 584,
+ "lightly_injured": 1083,
+ "kills": 595
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 5,
+ "displayed_kills": 18
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 5,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 10,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 7,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Troop skills": {
+ "Ambusher": {
+ "triggered": 1,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gordon_gwen_t9_probe_20260929/go_1000l_vs_20000t9.json
===================================================================
--- testcases/emulator_verified/gordon_gwen_t9_probe_20260929/go_1000l_vs_20000t9.json prev run
+++ testcases/emulator_verified/gordon_gwen_t9_probe_20260929/go_1000l_vs_20000t9.json this run
@@ -0,0 +1,150 @@
+[
+ {
+ "test_id": "go_1000l_vs_20000t9",
+ "description": "C2 battle report 2734693171525960",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 493.62,
+ "defense": 487.87,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t9": 20000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 258.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T10:33:21.000Z",
+ "randomseed": 1790678002,
+ "attacker": 0,
+ "defender": 19586,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171525960",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T10-33-37.111Z-abf53af6-2526-4ff0-b10e-eecbf5b1c1d4.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 145
+ },
+ "defender": {
+ "troops": 20000,
+ "survivors": 19586,
+ "losses": 0,
+ "injured": 145,
+ "lightly_injured": 269,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 145
+ }
+ },
+ "defender": {
+ "lancer_t9": {
+ "troops": 20000,
+ "survivors": 19586,
+ "losses": 0,
+ "injured": 145,
+ "lightly_injured": 269,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 2,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 5,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 3,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Troop skills": {
+ "Ambusher": {
+ "triggered": 2,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gordon_gwen_t9_probe_20260929/go_1000l_vs_20000t9l_10000t9m.json
===================================================================
--- testcases/emulator_verified/gordon_gwen_t9_probe_20260929/go_1000l_vs_20000t9l_10000t9m.json prev run
+++ testcases/emulator_verified/gordon_gwen_t9_probe_20260929/go_1000l_vs_20000t9l_10000t9m.json this run
@@ -0,0 +1,163 @@
+[
+ {
+ "test_id": "go_1000l_vs_20000t9l_10000t9m",
+ "description": "C2 battle report 2734693171526029",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 493.62,
+ "defense": 487.87,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t9": 20000,
+ "marksman_t9": 10000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 258.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T10:40:22.000Z",
+ "randomseed": 1790678423,
+ "attacker": 0,
+ "defender": 29805,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171526029",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T10-40-38.118Z-3fb7285e-018f-4026-8607-f7834e4fbac2.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 69
+ },
+ "defender": {
+ "troops": 30000,
+ "survivors": 29805,
+ "losses": 0,
+ "injured": 69,
+ "lightly_injured": 126,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 69
+ }
+ },
+ "defender": {
+ "lancer_t9": {
+ "troops": 20000,
+ "survivors": 19805,
+ "losses": 0,
+ "injured": 69,
+ "lightly_injured": 126,
+ "kills": 168
+ },
+ "marksman_t9": {
+ "troops": 10000,
+ "survivors": 10000,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 182
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 1,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 2,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 1,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Troop skills": {
+ "Ambusher": {
+ "triggered": 2,
+ "displayed_kills": null
+ },
+ "Volley": {
+ "triggered": 1,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gordon_gwen_t9_probe_20260929/go_60i_1000l_vs_20000t9.json
===================================================================
--- testcases/emulator_verified/gordon_gwen_t9_probe_20260929/go_60i_1000l_vs_20000t9.json prev run
+++ testcases/emulator_verified/gordon_gwen_t9_probe_20260929/go_60i_1000l_vs_20000t9.json this run
@@ -0,0 +1,159 @@
+[
+ {
+ "test_id": "go_60i_1000l_vs_20000t9",
+ "description": "C2 battle report 2734693171525981",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 60,
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 493.62,
+ "defense": 487.87,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t9": 20000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 258.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T10:34:53.000Z",
+ "randomseed": 1790678094,
+ "attacker": 0,
+ "defender": 19449,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171525981",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T10-35-09.110Z-573bbf53-ec59-495d-9d00-4a32c5eca9c6.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1060,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 371,
+ "lightly_injured": 689,
+ "kills": 193
+ },
+ "defender": {
+ "troops": 20000,
+ "survivors": 19449,
+ "losses": 0,
+ "injured": 193,
+ "lightly_injured": 358,
+ "kills": 371
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 60,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 21,
+ "lightly_injured": 39,
+ "kills": 1
+ },
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 192
+ }
+ },
+ "defender": {
+ "lancer_t9": {
+ "troops": 20000,
+ "survivors": 19449,
+ "losses": 0,
+ "injured": 193,
+ "lightly_injured": 358,
+ "kills": 371
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 3,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 6,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 4,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Troop skills": {
+ "Ambusher": {
+ "triggered": 3,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_renee_fresh_20260926/wip_1000l_1m_vs_3500l.json
===================================================================
--- testcases/emulator_verified/gwen_renee_fresh_20260926/wip_1000l_1m_vs_3500l.json prev run
+++ testcases/emulator_verified/gwen_renee_fresh_20260926/wip_1000l_1m_vs_3500l.json this run
@@ -0,0 +1,155 @@
+[
+ {
+ "test_id": "wip_1000l_1m_vs_3500l",
+ "description": "C2 battle report 2734693171371399",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 1000,
+ "marksman_t6": 1
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 344.4,
+ "defense": 333.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 3500
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 281.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 283.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T00:56:32.000Z",
+ "randomseed": 1790384193,
+ "attacker": 0,
+ "defender": 545,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171371399",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T00-57-00.243Z-11817d8c-b1ca-4dd8-9ece-dc6e05e7a552.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1001,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 351,
+ "lightly_injured": 650,
+ "kills": 1035
+ },
+ "defender": {
+ "troops": 3500,
+ "survivors": 545,
+ "losses": 0,
+ "injured": 1035,
+ "lightly_injured": 1920,
+ "kills": 351
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 999
+ },
+ "marksman_t6": {
+ "troops": 1,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 1,
+ "lightly_injured": 0,
+ "kills": 36
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 3500,
+ "survivors": 545,
+ "losses": 0,
+ "injured": 1035,
+ "lightly_injured": 1920,
+ "kills": 351
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 14,
+ "displayed_kills": 14
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 21,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_renee_fresh_20260926/wip_1000l_vs_3500l.json
===================================================================
--- testcases/emulator_verified/gwen_renee_fresh_20260926/wip_1000l_vs_3500l.json prev run
+++ testcases/emulator_verified/gwen_renee_fresh_20260926/wip_1000l_vs_3500l.json this run
@@ -0,0 +1,146 @@
+[
+ {
+ "test_id": "wip_1000l_vs_3500l",
+ "description": "C2 battle report 2734693171371279",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 344.4,
+ "defense": 333.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 3500
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 281.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 283.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T00:53:51.000Z",
+ "randomseed": 1790384032,
+ "attacker": 0,
+ "defender": 654,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171371279",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T00-54-19.178Z-707d1072-d904-43c8-85ad-61ffa7f296bb.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 997
+ },
+ "defender": {
+ "troops": 3500,
+ "survivors": 654,
+ "losses": 0,
+ "injured": 997,
+ "lightly_injured": 1849,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 997
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 3500,
+ "survivors": 654,
+ "losses": 0,
+ "injured": 997,
+ "lightly_injured": 1849,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 6,
+ "displayed_kills": 13
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 20,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_renee_fresh_20260926/wip_4000l_vs_300l.json
===================================================================
--- testcases/emulator_verified/gwen_renee_fresh_20260926/wip_4000l_vs_300l.json prev run
+++ testcases/emulator_verified/gwen_renee_fresh_20260926/wip_4000l_vs_300l.json this run
@@ -0,0 +1,146 @@
+[
+ {
+ "test_id": "wip_4000l_vs_300l",
+ "description": "C2 battle report 2734693171370798",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 4000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 344.4,
+ "defense": 333.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 281.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 283.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T00:42:04.000Z",
+ "randomseed": 1790383325,
+ "attacker": 3985,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171370798",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T00-42-32.138Z-b0d7db90-2e1c-4b93-866c-66a96d7c38b6.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 4000,
+ "survivors": 3985,
+ "losses": 0,
+ "injured": 6,
+ "lightly_injured": 9,
+ "kills": 105
+ },
+ "defender": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 6
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 4000,
+ "survivors": 3985,
+ "losses": 0,
+ "injured": 6,
+ "lightly_injured": 9,
+ "kills": 105
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 6
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 1,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_renee_fresh_20260926/wip_4000l_vs_400l.json
===================================================================
--- testcases/emulator_verified/gwen_renee_fresh_20260926/wip_4000l_vs_400l.json prev run
+++ testcases/emulator_verified/gwen_renee_fresh_20260926/wip_4000l_vs_400l.json this run
@@ -0,0 +1,146 @@
+[
+ {
+ "test_id": "wip_4000l_vs_400l",
+ "description": "C2 battle report 2734693171370827",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 4000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 344.4,
+ "defense": 333.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 400
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 281.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 283.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T00:43:47.000Z",
+ "randomseed": 1790383428,
+ "attacker": 3977,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171370827",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T00-44-15.152Z-6f0cd098-d02a-4642-b2e5-6de860cc40e6.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 4000,
+ "survivors": 3977,
+ "losses": 0,
+ "injured": 9,
+ "lightly_injured": 14,
+ "kills": 140
+ },
+ "defender": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 9
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 4000,
+ "survivors": 3977,
+ "losses": 0,
+ "injured": 9,
+ "lightly_injured": 14,
+ "kills": 140
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 9
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 2,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_renee_fresh_20260926/wip_4000l_vs_600l.json
===================================================================
--- testcases/emulator_verified/gwen_renee_fresh_20260926/wip_4000l_vs_600l.json prev run
+++ testcases/emulator_verified/gwen_renee_fresh_20260926/wip_4000l_vs_600l.json this run
@@ -0,0 +1,146 @@
+[
+ {
+ "test_id": "wip_4000l_vs_600l",
+ "description": "C2 battle report 2734693171371185",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 4000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 344.4,
+ "defense": 333.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 600
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 281.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 283.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T00:52:23.000Z",
+ "randomseed": 1790383944,
+ "attacker": 3958,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171371185",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T00-52-51.172Z-d1b17d0c-2e97-4d39-a00e-ee5a3285d69f.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 4000,
+ "survivors": 3958,
+ "losses": 0,
+ "injured": 15,
+ "lightly_injured": 27,
+ "kills": 210
+ },
+ "defender": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 15
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 4000,
+ "survivors": 3958,
+ "losses": 0,
+ "injured": 15,
+ "lightly_injured": 27,
+ "kills": 210
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 15
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 2,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_renee_skill_isolation/gwen_only_before_s2.json
===================================================================
--- testcases/emulator_verified/gwen_renee_skill_isolation/gwen_only_before_s2.json prev run
+++ testcases/emulator_verified/gwen_renee_skill_isolation/gwen_only_before_s2.json this run
@@ -0,0 +1,134 @@
+[
+ {
+ "test_id": "gwen_only_before_s2",
+ "description": "C2 battle report 2734693171349388",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 4000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 344.4,
+ "defense": 333.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 40
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T12:26:23.000Z",
+ "randomseed": 1790339184,
+ "attacker": 3994,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171349388",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T12-26-48.113Z-12b1a0b3-64b6-44a6-9d04-f7da316031f1.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 4000,
+ "survivors": 3994,
+ "losses": 0,
+ "injured": 3,
+ "lightly_injured": 3,
+ "kills": 14
+ },
+ "defender": {
+ "troops": 40,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 14,
+ "lightly_injured": 26,
+ "kills": 3
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 4000,
+ "survivors": 3994,
+ "losses": 0,
+ "injured": 3,
+ "lightly_injured": 3,
+ "kills": 14
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 40,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 14,
+ "lightly_injured": 26,
+ "kills": 3
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_renee_skill_isolation/gwen_renee_after_s2.json
===================================================================
--- testcases/emulator_verified/gwen_renee_skill_isolation/gwen_renee_after_s2.json prev run
+++ testcases/emulator_verified/gwen_renee_skill_isolation/gwen_renee_after_s2.json this run
@@ -0,0 +1,146 @@
+[
+ {
+ "test_id": "gwen_renee_after_s2",
+ "description": "C2 battle report 2734693171349608",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 4000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 344.4,
+ "defense": 333.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 1450
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T12:32:30.000Z",
+ "randomseed": 1790339551,
+ "attacker": 3865,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171349608",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T12-32-55.111Z-db52e872-7da9-4bb5-98c9-39935062834b.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 4000,
+ "survivors": 3865,
+ "losses": 0,
+ "injured": 48,
+ "lightly_injured": 87,
+ "kills": 508
+ },
+ "defender": {
+ "troops": 1450,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 508,
+ "lightly_injured": 942,
+ "kills": 48
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 4000,
+ "survivors": 3865,
+ "losses": 0,
+ "injured": 48,
+ "lightly_injured": 87,
+ "kills": 508
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 1450,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 508,
+ "lightly_injured": 942,
+ "kills": 48
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 1,
+ "displayed_kills": 8
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 3,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_renee_skill_isolation/gwen_renee_before_s2.json
===================================================================
--- testcases/emulator_verified/gwen_renee_skill_isolation/gwen_renee_before_s2.json prev run
+++ testcases/emulator_verified/gwen_renee_skill_isolation/gwen_renee_before_s2.json this run
@@ -0,0 +1,146 @@
+[
+ {
+ "test_id": "gwen_renee_before_s2",
+ "description": "C2 battle report 2734693171349444",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 4000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 344.4,
+ "defense": 333.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 800
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T12:28:46.000Z",
+ "randomseed": 1790339327,
+ "attacker": 3940,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171349444",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T12-29-11.127Z-19e7a713-0475-4f28-bfcf-4afa86e2c3ae.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 4000,
+ "survivors": 3940,
+ "losses": 0,
+ "injured": 21,
+ "lightly_injured": 39,
+ "kills": 280
+ },
+ "defender": {
+ "troops": 800,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 280,
+ "lightly_injured": 520,
+ "kills": 21
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 4000,
+ "survivors": 3940,
+ "losses": 0,
+ "injured": 21,
+ "lightly_injured": 39,
+ "kills": 280
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 800,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 280,
+ "lightly_injured": 520,
+ "kills": 21
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 2,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_renee_skill_isolation/gwen_renee_first_s2.json
===================================================================
--- testcases/emulator_verified/gwen_renee_skill_isolation/gwen_renee_first_s2.json prev run
+++ testcases/emulator_verified/gwen_renee_skill_isolation/gwen_renee_first_s2.json this run
@@ -0,0 +1,146 @@
+[
+ {
+ "test_id": "gwen_renee_first_s2",
+ "description": "C2 battle report 2734693171349554",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 4000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 344.4,
+ "defense": 333.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 1325
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T12:31:15.000Z",
+ "randomseed": 1790339476,
+ "attacker": 3883,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171349554",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T12-31-40.130Z-51202676-d0fa-47f5-bc5b-503fd496112a.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 4000,
+ "survivors": 3883,
+ "losses": 0,
+ "injured": 41,
+ "lightly_injured": 76,
+ "kills": 464
+ },
+ "defender": {
+ "troops": 1325,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 464,
+ "lightly_injured": 861,
+ "kills": 41
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 4000,
+ "survivors": 3883,
+ "losses": 0,
+ "injured": 41,
+ "lightly_injured": 76,
+ "kills": 464
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 1325,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 464,
+ "lightly_injured": 861,
+ "kills": 41
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 1,
+ "displayed_kills": 8
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 3,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_renee_skill_isolation/gwen_renee_mixed_after_s2.json
===================================================================
--- testcases/emulator_verified/gwen_renee_skill_isolation/gwen_renee_mixed_after_s2.json prev run
+++ testcases/emulator_verified/gwen_renee_skill_isolation/gwen_renee_mixed_after_s2.json this run
@@ -0,0 +1,155 @@
+[
+ {
+ "test_id": "gwen_renee_mixed_after_s2",
+ "description": "C2 battle report 2734693171349658",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 4000,
+ "marksman_t6": 1200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 344.4,
+ "defense": 333.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 2400
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T12:33:44.000Z",
+ "randomseed": 1790339625,
+ "attacker": 4978,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171349658",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T12-34-09.139Z-823db098-b05e-49e7-af2a-c5a4f03ef326.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 5200,
+ "survivors": 4978,
+ "losses": 0,
+ "injured": 78,
+ "lightly_injured": 144,
+ "kills": 840
+ },
+ "defender": {
+ "troops": 2400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 840,
+ "lightly_injured": 1560,
+ "kills": 78
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 4000,
+ "survivors": 3778,
+ "losses": 0,
+ "injured": 78,
+ "lightly_injured": 144,
+ "kills": 622
+ },
+ "marksman_t6": {
+ "troops": 1200,
+ "survivors": 1200,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 218
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 2400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 840,
+ "lightly_injured": 1560,
+ "kills": 78
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 2,
+ "displayed_kills": 16
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 3,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_renee_skill_isolation/gwen_renee_mixed_before_s2.json
===================================================================
--- testcases/emulator_verified/gwen_renee_skill_isolation/gwen_renee_mixed_before_s2.json prev run
+++ testcases/emulator_verified/gwen_renee_skill_isolation/gwen_renee_mixed_before_s2.json this run
@@ -0,0 +1,155 @@
+[
+ {
+ "test_id": "gwen_renee_mixed_before_s2",
+ "description": "C2 battle report 2734693171349532",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 4000,
+ "marksman_t6": 1200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 344.4,
+ "defense": 333.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 1200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T12:29:59.000Z",
+ "randomseed": 1790339400,
+ "attacker": 5113,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171349532",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T12-30-24.115Z-f7e369e2-74c5-4600-88a6-d3eee1e67f5d.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 5200,
+ "survivors": 5113,
+ "losses": 0,
+ "injured": 31,
+ "lightly_injured": 56,
+ "kills": 420
+ },
+ "defender": {
+ "troops": 1200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 420,
+ "lightly_injured": 780,
+ "kills": 31
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 4000,
+ "survivors": 3913,
+ "losses": 0,
+ "injured": 31,
+ "lightly_injured": 56,
+ "kills": 328
+ },
+ "marksman_t6": {
+ "troops": 1200,
+ "survivors": 1200,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 92
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 1200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 420,
+ "lightly_injured": 780,
+ "kills": 31
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 2,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_renee_skill_isolation/renee_only_short.json
===================================================================
--- testcases/emulator_verified/gwen_renee_skill_isolation/renee_only_short.json prev run
+++ testcases/emulator_verified/gwen_renee_skill_isolation/renee_only_short.json this run
@@ -0,0 +1,135 @@
+[
+ {
+ "test_id": "renee_only_short",
+ "description": "C2 battle report 2734693171349427",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 4000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 800
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T12:27:34.000Z",
+ "randomseed": 1790339255,
+ "attacker": 3940,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171349427",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T12-27-59.114Z-8b56b7fd-a65b-4e20-8561-3adaf9057432.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 4000,
+ "survivors": 3940,
+ "losses": 0,
+ "injured": 21,
+ "lightly_injured": 39,
+ "kills": 280
+ },
+ "defender": {
+ "troops": 800,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 280,
+ "lightly_injured": 520,
+ "kills": 21
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 4000,
+ "survivors": 3940,
+ "losses": 0,
+ "injured": 21,
+ "lightly_injured": 39,
+ "kills": 280
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 800,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 280,
+ "lightly_injured": 520,
+ "kills": 21
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 2,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_renee_wall_20260926/renee_500l_vs_gatot_300i.json
===================================================================
--- testcases/emulator_verified/gwen_renee_wall_20260926/renee_500l_vs_gatot_300i.json prev run
+++ testcases/emulator_verified/gwen_renee_wall_20260926/renee_500l_vs_gatot_300i.json this run
@@ -0,0 +1,149 @@
+[
+ {
+ "test_id": "renee_500l_vs_gatot_300i",
+ "description": "C2 battle report 2734693171382525",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 500
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 607.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 283.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gatot": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t9": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 627.31,
+ "defense": 619.86,
+ "lethality": 230.66,
+ "health": 281.02
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T07:19:19.000Z",
+ "randomseed": 1790407160,
+ "attacker": 0,
+ "defender": 229,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171382525",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T07-19-36.111Z-b0079f80-ea2a-4d05-a677-657dffdf50e6.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 25
+ },
+ "defender": {
+ "troops": 300,
+ "survivors": 229,
+ "losses": 0,
+ "injured": 25,
+ "lightly_injured": 46,
+ "kills": 175
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 25
+ }
+ },
+ "defender": {
+ "infantry_t9": {
+ "troops": 300,
+ "survivors": 229,
+ "losses": 0,
+ "injured": 25,
+ "lightly_injured": 46,
+ "kills": 175
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 49,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": 98,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_renee_wall_20260926/renee_gwen_500l_vs_gatot_300i.json
===================================================================
--- testcases/emulator_verified/gwen_renee_wall_20260926/renee_gwen_500l_vs_gatot_300i.json prev run
+++ testcases/emulator_verified/gwen_renee_wall_20260926/renee_gwen_500l_vs_gatot_300i.json this run
@@ -0,0 +1,165 @@
+[
+ {
+ "test_id": "renee_gwen_500l_vs_gatot_300i",
+ "description": "C2 battle report 2734693171382576",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 500
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 607.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 434.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gatot": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t9": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 627.31,
+ "defense": 619.86,
+ "lethality": 230.66,
+ "health": 281.02
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T07:21:54.000Z",
+ "randomseed": 1790407315,
+ "attacker": 0,
+ "defender": 215,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171382576",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T07-22-11.111Z-0a1cc51e-2f29-4036-8fb6-dfb231bc018d.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 30
+ },
+ "defender": {
+ "troops": 300,
+ "survivors": 215,
+ "losses": 0,
+ "injured": 30,
+ "lightly_injured": 55,
+ "kills": 175
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 30
+ }
+ },
+ "defender": {
+ "infantry_t9": {
+ "troops": 300,
+ "survivors": 215,
+ "losses": 0,
+ "injured": 30,
+ "lightly_injured": 55,
+ "kills": 175
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 16,
+ "displayed_kills": null
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 50,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": 100,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/model_checks_20260929/c10_gwen_300l_vs_31000t9l.json
===================================================================
--- testcases/emulator_verified/model_checks_20260929/c10_gwen_300l_vs_31000t9l.json prev run
+++ testcases/emulator_verified/model_checks_20260929/c10_gwen_300l_vs_31000t9l.json this run
@@ -0,0 +1,146 @@
+[
+ {
+ "test_id": "c10_gwen_300l_vs_31000t9l",
+ "description": "C2 battle report 2734693171529366",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 435.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t9": 31000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 258.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T13:18:38.000Z",
+ "randomseed": 1790687919,
+ "attacker": 0,
+ "defender": 30981,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171529366",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T13-18-48.158Z-59acb471-6ae9-46a5-9e85-b7c06f5106e9.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 7
+ },
+ "defender": {
+ "troops": 31000,
+ "survivors": 30981,
+ "losses": 0,
+ "injured": 7,
+ "lightly_injured": 12,
+ "kills": 105
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 7
+ }
+ },
+ "defender": {
+ "lancer_t9": {
+ "troops": 31000,
+ "survivors": 30981,
+ "losses": 0,
+ "injured": 7,
+ "lightly_injured": 12,
+ "kills": 105
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": null,
+ "displayed_kills": null
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Troop skills": {
+ "Ambusher": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/model_checks_20260929/c11a_bradley_norah_1500l_vs_2000l_3000m.json
===================================================================
--- testcases/emulator_verified/model_checks_20260929/c11a_bradley_norah_1500l_vs_2000l_3000m.json prev run
+++ testcases/emulator_verified/model_checks_20260929/c11a_bradley_norah_1500l_vs_2000l_3000m.json this run
@@ -0,0 +1,160 @@
+[
+ {
+ "test_id": "c11a_bradley_norah_1500l_vs_2000l_3000m",
+ "description": "C2 battle report 2734693171530112",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Norah": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ },
+ "Bradley": {
+ "skill_1": 5,
+ "skill_2": 4,
+ "skill_3": 3,
+ "skill_4": 1
+ }
+ },
+ "troops": {
+ "lancer_t6": 1500
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 458.79,
+ "defense": 453.04,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 739.42,
+ "defense": 735.42,
+ "lethality": 375.19,
+ "health": 331.96
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 2000,
+ "marksman_t6": 3000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 258.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T13:47:06.000Z",
+ "randomseed": 1790689627,
+ "attacker": 0,
+ "defender": 1410,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171530112",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T13-47-22.150Z-6fd15df0-004f-4d85-ba66-56c665819418.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 525,
+ "lightly_injured": 975,
+ "kills": 1257
+ },
+ "defender": {
+ "troops": 5000,
+ "survivors": 1410,
+ "losses": 0,
+ "injured": 1257,
+ "lightly_injured": 2333,
+ "kills": 525
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 525,
+ "lightly_injured": 975,
+ "kills": 1257
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 2000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 700,
+ "lightly_injured": 1300,
+ "kills": 86
+ },
+ "marksman_t6": {
+ "troops": 3000,
+ "survivors": 1410,
+ "losses": 0,
+ "injured": 557,
+ "lightly_injured": 1033,
+ "kills": 439
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Norah": {
+ "Sneak Strike": {
+ "triggered": 3,
+ "displayed_kills": 52
+ },
+ "Momentum": {
+ "triggered": 6,
+ "displayed_kills": null
+ }
+ },
+ "Bradley": {
+ "Tactical Assistance": {
+ "triggered": 8,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/model_checks_20260929/c11b_bradley_norah_1500l_vs_2000l_3000m.json
===================================================================
--- testcases/emulator_verified/model_checks_20260929/c11b_bradley_norah_1500l_vs_2000l_3000m.json prev run
+++ testcases/emulator_verified/model_checks_20260929/c11b_bradley_norah_1500l_vs_2000l_3000m.json this run
@@ -0,0 +1,160 @@
+[
+ {
+ "test_id": "c11b_bradley_norah_1500l_vs_2000l_3000m",
+ "description": "C2 battle report 2734693171530118",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Norah": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ },
+ "Bradley": {
+ "skill_1": 5,
+ "skill_2": 4,
+ "skill_3": 3,
+ "skill_4": 1
+ }
+ },
+ "troops": {
+ "lancer_t6": 1500
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 458.79,
+ "defense": 453.04,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 739.42,
+ "defense": 735.42,
+ "lethality": 375.19,
+ "health": 331.96
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 2000,
+ "marksman_t6": 3000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 258.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T13:47:52.000Z",
+ "randomseed": 1790689673,
+ "attacker": 0,
+ "defender": 140,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171530118",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T13-48-08.136Z-e845c869-615d-478f-b026-1abc0175931e.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 525,
+ "lightly_injured": 975,
+ "kills": 1701
+ },
+ "defender": {
+ "troops": 5000,
+ "survivors": 140,
+ "losses": 0,
+ "injured": 1701,
+ "lightly_injured": 3159,
+ "kills": 525
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 525,
+ "lightly_injured": 975,
+ "kills": 1701
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 2000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 700,
+ "lightly_injured": 1300,
+ "kills": 77
+ },
+ "marksman_t6": {
+ "troops": 3000,
+ "survivors": 140,
+ "losses": 0,
+ "injured": 1001,
+ "lightly_injured": 1859,
+ "kills": 448
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Norah": {
+ "Sneak Strike": {
+ "triggered": 9,
+ "displayed_kills": 238
+ },
+ "Momentum": {
+ "triggered": 8,
+ "displayed_kills": null
+ }
+ },
+ "Bradley": {
+ "Tactical Assistance": {
+ "triggered": 11,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/model_checks_20260929/c1a_bradley_norah_1500l_vs_2000i_3000m.json
===================================================================
--- testcases/emulator_verified/model_checks_20260929/c1a_bradley_norah_1500l_vs_2000i_3000m.json prev run
+++ testcases/emulator_verified/model_checks_20260929/c1a_bradley_norah_1500l_vs_2000i_3000m.json this run
@@ -0,0 +1,160 @@
+[
+ {
+ "test_id": "c1a_bradley_norah_1500l_vs_2000i_3000m",
+ "description": "C2 battle report 2734693171529313",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Norah": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ },
+ "Bradley": {
+ "skill_1": 5,
+ "skill_2": 4,
+ "skill_3": 3,
+ "skill_4": 1
+ }
+ },
+ "troops": {
+ "lancer_t6": 1500
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 458.79,
+ "defense": 453.04,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 739.42,
+ "defense": 735.42,
+ "lethality": 375.19,
+ "health": 331.96
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 2000,
+ "marksman_t6": 3000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 258.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T13:14:37.000Z",
+ "randomseed": 1790687678,
+ "attacker": 0,
+ "defender": 3613,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171529313",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T13-14-53.142Z-31d86c8d-9b98-4d1b-9d77-90d2e6cf85b2.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 525,
+ "lightly_injured": 975,
+ "kills": 486
+ },
+ "defender": {
+ "troops": 5000,
+ "survivors": 3613,
+ "losses": 0,
+ "injured": 486,
+ "lightly_injured": 901,
+ "kills": 525
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 525,
+ "lightly_injured": 975,
+ "kills": 486
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 2000,
+ "survivors": 1004,
+ "losses": 0,
+ "injured": 349,
+ "lightly_injured": 647,
+ "kills": 93
+ },
+ "marksman_t6": {
+ "troops": 3000,
+ "survivors": 2609,
+ "losses": 0,
+ "injured": 137,
+ "lightly_injured": 254,
+ "kills": 432
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Norah": {
+ "Sneak Strike": {
+ "triggered": 7,
+ "displayed_kills": 166
+ },
+ "Momentum": {
+ "triggered": 6,
+ "displayed_kills": null
+ }
+ },
+ "Bradley": {
+ "Tactical Assistance": {
+ "triggered": 7,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/model_checks_20260929/c1b_bradley_norah_1500l_vs_2000i_3000m.json
===================================================================
--- testcases/emulator_verified/model_checks_20260929/c1b_bradley_norah_1500l_vs_2000i_3000m.json prev run
+++ testcases/emulator_verified/model_checks_20260929/c1b_bradley_norah_1500l_vs_2000i_3000m.json this run
@@ -0,0 +1,160 @@
+[
+ {
+ "test_id": "c1b_bradley_norah_1500l_vs_2000i_3000m",
+ "description": "C2 battle report 2734693171529322",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Norah": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ },
+ "Bradley": {
+ "skill_1": 5,
+ "skill_2": 4,
+ "skill_3": 3,
+ "skill_4": 1
+ }
+ },
+ "troops": {
+ "lancer_t6": 1500
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 458.79,
+ "defense": 453.04,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 739.42,
+ "defense": 735.42,
+ "lethality": 375.19,
+ "health": 331.96
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 2000,
+ "marksman_t6": 3000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 258.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T13:15:22.000Z",
+ "randomseed": 1790687723,
+ "attacker": 0,
+ "defender": 3641,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171529322",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T13-15-38.124Z-d2b3c33c-8cf7-4134-814b-bb247dee7410.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 525,
+ "lightly_injured": 975,
+ "kills": 477
+ },
+ "defender": {
+ "troops": 5000,
+ "survivors": 3641,
+ "losses": 0,
+ "injured": 477,
+ "lightly_injured": 882,
+ "kills": 525
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 525,
+ "lightly_injured": 975,
+ "kills": 477
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 2000,
+ "survivors": 1011,
+ "losses": 0,
+ "injured": 347,
+ "lightly_injured": 642,
+ "kills": 93
+ },
+ "marksman_t6": {
+ "troops": 3000,
+ "survivors": 2630,
+ "losses": 0,
+ "injured": 130,
+ "lightly_injured": 240,
+ "kills": 432
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Norah": {
+ "Sneak Strike": {
+ "triggered": 7,
+ "displayed_kills": 157
+ },
+ "Momentum": {
+ "triggered": 6,
+ "displayed_kills": null
+ }
+ },
+ "Bradley": {
+ "Tactical Assistance": {
+ "triggered": 7,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/model_checks_20260929/c2_bradley_800m_vs_1000i_1200m.json
===================================================================
--- testcases/emulator_verified/model_checks_20260929/c2_bradley_800m_vs_1000i_1200m.json prev run
+++ testcases/emulator_verified/model_checks_20260929/c2_bradley_800m_vs_1000i_1200m.json this run
@@ -0,0 +1,144 @@
+[
+ {
+ "test_id": "c2_bradley_800m_vs_1000i_1200m",
+ "description": "C2 battle report 2734693171529139",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Bradley": {
+ "skill_1": 5,
+ "skill_2": 4,
+ "skill_3": 3,
+ "skill_4": 1
+ }
+ },
+ "troops": {
+ "marksman_t6": 800
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 739.42,
+ "defense": 735.42,
+ "lethality": 375.19,
+ "health": 331.96
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 1000,
+ "marksman_t6": 1200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 258.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T13:08:35.000Z",
+ "randomseed": 1790687316,
+ "attacker": 507,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171529139",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T13-08-51.144Z-4c98341d-7512-4f4c-8ab1-cc1727c14ea5.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 800,
+ "survivors": 507,
+ "losses": 0,
+ "injured": 103,
+ "lightly_injured": 190,
+ "kills": 770
+ },
+ "defender": {
+ "troops": 2200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 770,
+ "lightly_injured": 1430,
+ "kills": 103
+ },
+ "troop_details": {
+ "attacker": {
+ "marksman_t6": {
+ "troops": 800,
+ "survivors": 507,
+ "losses": 0,
+ "injured": 103,
+ "lightly_injured": 190,
+ "kills": 770
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 12
+ },
+ "marksman_t6": {
+ "troops": 1200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 420,
+ "lightly_injured": 780,
+ "kills": 91
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Bradley": {
+ "Tactical Assistance": {
+ "triggered": 5,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/model_checks_20260929/c3_renee_gwen_1000l_vs_8000t9m.json
===================================================================
--- testcases/emulator_verified/model_checks_20260929/c3_renee_gwen_1000l_vs_8000t9m.json prev run
+++ testcases/emulator_verified/model_checks_20260929/c3_renee_gwen_1000l_vs_8000t9m.json this run
@@ -0,0 +1,158 @@
+[
+ {
+ "test_id": "c3_renee_gwen_1000l_vs_8000t9m",
+ "description": "C2 battle report 2734693171529178",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 607.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 435.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "marksman_t9": 8000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 258.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T13:09:20.000Z",
+ "randomseed": 1790687361,
+ "attacker": 0,
+ "defender": 6227,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171529178",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T13-09-36.121Z-513dcf5d-014f-4802-970c-1e6138be8711.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 621
+ },
+ "defender": {
+ "troops": 8000,
+ "survivors": 6227,
+ "losses": 0,
+ "injured": 621,
+ "lightly_injured": 1152,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 621
+ }
+ },
+ "defender": {
+ "marksman_t9": {
+ "troops": 8000,
+ "survivors": 6227,
+ "losses": 0,
+ "injured": 621,
+ "lightly_injured": 1152,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 2,
+ "displayed_kills": 6
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 7,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Troop skills": {
+ "Volley": {
+ "triggered": 1,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/model_checks_20260929/c4_renee_1000l_vs_8000t9m.json
===================================================================
--- testcases/emulator_verified/model_checks_20260929/c4_renee_1000l_vs_8000t9m.json prev run
+++ testcases/emulator_verified/model_checks_20260929/c4_renee_1000l_vs_8000t9m.json this run
@@ -0,0 +1,142 @@
+[
+ {
+ "test_id": "c4_renee_1000l_vs_8000t9m",
+ "description": "C2 battle report 2734693171529191",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 607.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "marksman_t9": 8000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 258.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T13:10:06.000Z",
+ "randomseed": 1790687407,
+ "attacker": 0,
+ "defender": 6427,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171529191",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T13-10-22.147Z-d8f4dd30-b5e2-4904-a898-82a2465e998d.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 551
+ },
+ "defender": {
+ "troops": 8000,
+ "survivors": 6427,
+ "losses": 0,
+ "injured": 551,
+ "lightly_injured": 1022,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 551
+ }
+ },
+ "defender": {
+ "marksman_t9": {
+ "troops": 8000,
+ "survivors": 6427,
+ "losses": 0,
+ "injured": 551,
+ "lightly_injured": 1022,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 7,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Troop skills": {
+ "Volley": {
+ "triggered": 2,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/model_checks_20260929/c5_gordon_1000l_vs_gatot_3000i.json
===================================================================
--- testcases/emulator_verified/model_checks_20260929/c5_gordon_1000l_vs_gatot_3000i.json prev run
+++ testcases/emulator_verified/model_checks_20260929/c5_gordon_1000l_vs_gatot_3000i.json this run
@@ -0,0 +1,157 @@
+[
+ {
+ "test_id": "c5_gordon_1000l_vs_gatot_3000i",
+ "description": "C2 battle report 2734693171529201",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 493.62,
+ "defense": 487.87,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gatot": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 3000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 628.31,
+ "defense": 619.86,
+ "lethality": 230.66,
+ "health": 281.02
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T13:10:51.000Z",
+ "randomseed": 1790687452,
+ "attacker": 0,
+ "defender": 2944,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171529201",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T13-11-07.126Z-178d1f4d-9978-4ab1-9c00-b78bd19c38ea.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 20
+ },
+ "defender": {
+ "troops": 3000,
+ "survivors": 2944,
+ "losses": 0,
+ "injured": 20,
+ "lightly_injured": 36,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 20
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 3000,
+ "survivors": 2944,
+ "losses": 0,
+ "injured": 20,
+ "lightly_injured": 36,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 11,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 23,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 15,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": 46,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/model_checks_20260929/c6_gordon_1000l_vs_10000t9m.json
===================================================================
--- testcases/emulator_verified/model_checks_20260929/c6_gordon_1000l_vs_10000t9m.json prev run
+++ testcases/emulator_verified/model_checks_20260929/c6_gordon_1000l_vs_10000t9m.json this run
@@ -0,0 +1,150 @@
+[
+ {
+ "test_id": "c6_gordon_1000l_vs_10000t9m",
+ "description": "C2 battle report 2734693171529227",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 493.62,
+ "defense": 487.87,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "marksman_t9": 10000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 258.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T13:11:36.000Z",
+ "randomseed": 1790687497,
+ "attacker": 0,
+ "defender": 9376,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171529227",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T13-11-52.138Z-0b2481f1-89f2-46b7-aca1-2bebd94b7c45.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 219
+ },
+ "defender": {
+ "troops": 10000,
+ "survivors": 9376,
+ "losses": 0,
+ "injured": 219,
+ "lightly_injured": 405,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 219
+ }
+ },
+ "defender": {
+ "marksman_t9": {
+ "troops": 10000,
+ "survivors": 9376,
+ "losses": 0,
+ "injured": 219,
+ "lightly_injured": 405,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 2,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 5,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 3,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Troop skills": {
+ "Volley": {
+ "triggered": 1,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/model_checks_20260929/c7_noheroes_1000l_vs_10000t9m.json
===================================================================
--- testcases/emulator_verified/model_checks_20260929/c7_noheroes_1000l_vs_10000t9m.json prev run
+++ testcases/emulator_verified/model_checks_20260929/c7_noheroes_1000l_vs_10000t9m.json this run
@@ -0,0 +1,128 @@
+[
+ {
+ "test_id": "c7_noheroes_1000l_vs_10000t9m",
+ "description": "C2 battle report 2734693171529242",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "marksman_t9": 10000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 258.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T13:12:21.000Z",
+ "randomseed": 1790687542,
+ "attacker": 0,
+ "defender": 9840,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171529242",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T13-12-37.119Z-fad2388e-0d80-4d88-a5da-5ac6ee33c272.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 56
+ },
+ "defender": {
+ "troops": 10000,
+ "survivors": 9840,
+ "losses": 0,
+ "injured": 56,
+ "lightly_injured": 104,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 56
+ }
+ },
+ "defender": {
+ "marksman_t9": {
+ "troops": 10000,
+ "survivors": 9840,
+ "losses": 0,
+ "injured": 56,
+ "lightly_injured": 104,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {
+ "Troop skills": {
+ "Volley": {
+ "triggered": 2,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/model_checks_20260929/c8_gordon_gwen_300l_vs_31000t9l.json
===================================================================
--- testcases/emulator_verified/model_checks_20260929/c8_gordon_gwen_300l_vs_31000t9l.json prev run
+++ testcases/emulator_verified/model_checks_20260929/c8_gordon_gwen_300l_vs_31000t9l.json this run
@@ -0,0 +1,166 @@
+[
+ {
+ "test_id": "c8_gordon_gwen_300l_vs_31000t9l",
+ "description": "C2 battle report 2734693171529259",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 493.62,
+ "defense": 487.87,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 435.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t9": 31000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 258.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T13:13:06.000Z",
+ "randomseed": 1790687587,
+ "attacker": 0,
+ "defender": 30939,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171529259",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T13-13-22.148Z-4dd40c8b-9c3d-490b-98c5-7af3eed54aa3.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 22
+ },
+ "defender": {
+ "troops": 31000,
+ "survivors": 30939,
+ "losses": 0,
+ "injured": 22,
+ "lightly_injured": 39,
+ "kills": 105
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 22
+ }
+ },
+ "defender": {
+ "lancer_t9": {
+ "troops": 31000,
+ "survivors": 30939,
+ "losses": 0,
+ "injured": 22,
+ "lightly_injured": 39,
+ "kills": 105
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": null,
+ "displayed_kills": null
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 1,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 2,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 1,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Troop skills": {
+ "Ambusher": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/model_checks_20260929/c9_gordon_300l_vs_31000t9l.json
===================================================================
--- testcases/emulator_verified/model_checks_20260929/c9_gordon_300l_vs_31000t9l.json prev run
+++ testcases/emulator_verified/model_checks_20260929/c9_gordon_300l_vs_31000t9l.json this run
@@ -0,0 +1,150 @@
+[
+ {
+ "test_id": "c9_gordon_300l_vs_31000t9l",
+ "description": "C2 battle report 2734693171529360",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 493.62,
+ "defense": 487.87,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t9": 31000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 258.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T13:17:52.000Z",
+ "randomseed": 1790687873,
+ "attacker": 0,
+ "defender": 30946,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171529360",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T13-18-08.153Z-849cc59e-a541-48ac-9bdd-7c0229e92cee.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 19
+ },
+ "defender": {
+ "troops": 31000,
+ "survivors": 30946,
+ "losses": 0,
+ "injured": 19,
+ "lightly_injured": 35,
+ "kills": 105
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 19
+ }
+ },
+ "defender": {
+ "lancer_t9": {
+ "troops": 31000,
+ "survivors": 30946,
+ "losses": 0,
+ "injured": 19,
+ "lightly_injured": 35,
+ "kills": 105
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 1,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 2,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 1,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Troop skills": {
+ "Ambusher": {
+ "triggered": 1,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/remaining_parity_20260929/natalia_mixed_control.json
===================================================================
--- testcases/emulator_verified/remaining_parity_20260929/natalia_mixed_control.json prev run
+++ testcases/emulator_verified/remaining_parity_20260929/natalia_mixed_control.json this run
@@ -0,0 +1,619 @@
+[
+ {
+ "test_id": "natalia_mixed_control",
+ "description": "C2 battle report 2734693171534211",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Sergey": {
+ "skill_1": 2,
+ "skill_2": 2
+ },
+ "Molly": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_4": 0
+ },
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 800,
+ "lancer_t6": 250,
+ "marksman_t6": 700
+ },
+ "stats": {
+ "infantry": {
+ "attack": 347.14,
+ "defense": 338.69,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 259.81,
+ "defense": 259.11,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 297.4,
+ "defense": 286.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Natalia": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 200,
+ "lancer_t6": 100,
+ "marksman_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 346.18,
+ "defense": 334.18,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T15:53:51.000Z",
+ "randomseed": 1790697232,
+ "attacker": 1644,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171534211",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T15-54-16.107Z-1937034a-2806-4a67-9bd8-64ad000f820e.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1750,
+ "survivors": 1644,
+ "losses": 0,
+ "injured": 38,
+ "lightly_injured": 68,
+ "kills": 140
+ },
+ "defender": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 38
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 800,
+ "survivors": 694,
+ "losses": 0,
+ "injured": 38,
+ "lightly_injured": 68,
+ "kills": 28
+ },
+ "lancer_t6": {
+ "troops": 250,
+ "survivors": 250,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 35
+ },
+ "marksman_t6": {
+ "troops": 700,
+ "survivors": 700,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 77
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 5
+ },
+ "lancer_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 12
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 21
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Molly": {
+ "Ice Dominion": {
+ "triggered": 37,
+ "displayed_kills": 15
+ },
+ "Snow's Grace": {
+ "triggered": 11,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Natalia": {
+ "Feral Protection": {
+ "triggered": 8,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-29T15:54:55.000Z",
+ "randomseed": 1790697296,
+ "attacker": 1639,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171534240",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T15-55-20.110Z-106c1a4d-2e3c-405b-911c-d41b1b9b3300.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1750,
+ "survivors": 1639,
+ "losses": 0,
+ "injured": 39,
+ "lightly_injured": 72,
+ "kills": 140
+ },
+ "defender": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 39
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 800,
+ "survivors": 689,
+ "losses": 0,
+ "injured": 39,
+ "lightly_injured": 72,
+ "kills": 25
+ },
+ "lancer_t6": {
+ "troops": 250,
+ "survivors": 250,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 35
+ },
+ "marksman_t6": {
+ "troops": 700,
+ "survivors": 700,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 80
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 5
+ },
+ "lancer_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 12
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 22
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Molly": {
+ "Ice Dominion": {
+ "triggered": 36,
+ "displayed_kills": 14
+ },
+ "Snow's Grace": {
+ "triggered": 11,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Natalia": {
+ "Feral Protection": {
+ "triggered": 10,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-29T15:55:59.000Z",
+ "randomseed": 1790697360,
+ "attacker": 1640,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171534267",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T15-56-24.102Z-abcb3f3e-98b7-4cb3-b250-b01d8de3ad40.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1750,
+ "survivors": 1640,
+ "losses": 0,
+ "injured": 39,
+ "lightly_injured": 71,
+ "kills": 140
+ },
+ "defender": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 39
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 800,
+ "survivors": 690,
+ "losses": 0,
+ "injured": 39,
+ "lightly_injured": 71,
+ "kills": 26
+ },
+ "lancer_t6": {
+ "troops": 250,
+ "survivors": 250,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 36
+ },
+ "marksman_t6": {
+ "troops": 700,
+ "survivors": 700,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 78
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 5
+ },
+ "lancer_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 12
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 22
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Molly": {
+ "Ice Dominion": {
+ "triggered": 37,
+ "displayed_kills": 12
+ },
+ "Snow's Grace": {
+ "triggered": 12,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Natalia": {
+ "Feral Protection": {
+ "triggered": 10,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-29T15:57:03.000Z",
+ "randomseed": 1790697424,
+ "attacker": 1637,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171534275",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T15-57-28.100Z-9a9a157d-357b-4ff6-a397-a9115011cd67.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1750,
+ "survivors": 1637,
+ "losses": 0,
+ "injured": 40,
+ "lightly_injured": 73,
+ "kills": 140
+ },
+ "defender": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 40
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 800,
+ "survivors": 687,
+ "losses": 0,
+ "injured": 40,
+ "lightly_injured": 73,
+ "kills": 23
+ },
+ "lancer_t6": {
+ "troops": 250,
+ "survivors": 250,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 35
+ },
+ "marksman_t6": {
+ "troops": 700,
+ "survivors": 700,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 82
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 6
+ },
+ "lancer_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 13
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 21
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Molly": {
+ "Ice Dominion": {
+ "triggered": 30,
+ "displayed_kills": 9
+ },
+ "Snow's Grace": {
+ "triggered": 9,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Natalia": {
+ "Feral Protection": {
+ "triggered": 9,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-29T15:58:07.000Z",
+ "randomseed": 1790697488,
+ "attacker": 1646,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171534283",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T15-58-32.102Z-40aa4d17-bf4a-471e-b74f-d537b68e9138.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1750,
+ "survivors": 1646,
+ "losses": 0,
+ "injured": 37,
+ "lightly_injured": 67,
+ "kills": 140
+ },
+ "defender": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 37
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 800,
+ "survivors": 696,
+ "losses": 0,
+ "injured": 37,
+ "lightly_injured": 67,
+ "kills": 26
+ },
+ "lancer_t6": {
+ "troops": 250,
+ "survivors": 250,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 35
+ },
+ "marksman_t6": {
+ "troops": 700,
+ "survivors": 700,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 79
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 5
+ },
+ "lancer_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 11
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 21
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Molly": {
+ "Ice Dominion": {
+ "triggered": 34,
+ "displayed_kills": 10
+ },
+ "Snow's Grace": {
+ "triggered": 14,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Natalia": {
+ "Feral Protection": {
+ "triggered": 9,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/remaining_parity_20260929/six_hero_mixed_control.json
===================================================================
--- testcases/emulator_verified/remaining_parity_20260929/six_hero_mixed_control.json prev run
+++ testcases/emulator_verified/remaining_parity_20260929/six_hero_mixed_control.json this run
@@ -0,0 +1,699 @@
+[
+ {
+ "test_id": "six_hero_mixed_control",
+ "description": "C2 battle report 2734693171534393",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Sergey": {
+ "skill_1": 2,
+ "skill_2": 2
+ },
+ "Molly": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_4": 0
+ },
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 800,
+ "lancer_t6": 250,
+ "marksman_t6": 700
+ },
+ "stats": {
+ "infantry": {
+ "attack": 347.14,
+ "defense": 338.69,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 259.81,
+ "defense": 259.11,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 297.4,
+ "defense": 286.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Natalia": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_4": 0
+ },
+ "Patrick": {
+ "skill_1": 3,
+ "skill_2": 2
+ },
+ "Alonso": {
+ "skill_1": 3,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 200,
+ "lancer_t6": 100,
+ "marksman_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 346.18,
+ "defense": 334.18,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 396.69,
+ "defense": 395.94,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 410.06,
+ "defense": 411.06,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T16:02:59.000Z",
+ "randomseed": 1790697780,
+ "attacker": 1494,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171534393",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T16-03-24.105Z-ddf53900-7b4d-484a-bb12-aa89c5294599.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1750,
+ "survivors": 1494,
+ "losses": 0,
+ "injured": 90,
+ "lightly_injured": 166,
+ "kills": 140
+ },
+ "defender": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 90
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 800,
+ "survivors": 544,
+ "losses": 0,
+ "injured": 90,
+ "lightly_injured": 166,
+ "kills": 18
+ },
+ "lancer_t6": {
+ "troops": 250,
+ "survivors": 250,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 36
+ },
+ "marksman_t6": {
+ "troops": 700,
+ "survivors": 700,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 86
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 9
+ },
+ "lancer_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 28
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 53
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Molly": {
+ "Ice Dominion": {
+ "triggered": 51,
+ "displayed_kills": 14
+ },
+ "Snow's Grace": {
+ "triggered": 9,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Alonso": {
+ "Poison Harpoon": {
+ "triggered": 40,
+ "displayed_kills": 14
+ },
+ "Iron Strength": {
+ "triggered": 22,
+ "displayed_kills": null
+ },
+ "Onslaught": {
+ "triggered": 12,
+ "displayed_kills": null
+ }
+ },
+ "Natalia": {
+ "Feral Protection": {
+ "triggered": 13,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-29T16:04:03.000Z",
+ "randomseed": 1790697844,
+ "attacker": 1501,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171534417",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T16-04-28.098Z-4aaf9dd2-899b-4127-9132-c4a30171c39c.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1750,
+ "survivors": 1501,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 161,
+ "kills": 140
+ },
+ "defender": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 88
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 800,
+ "survivors": 551,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 161,
+ "kills": 20
+ },
+ "lancer_t6": {
+ "troops": 250,
+ "survivors": 250,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 38
+ },
+ "marksman_t6": {
+ "troops": 700,
+ "survivors": 700,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 82
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 9
+ },
+ "lancer_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 28
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 51
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Molly": {
+ "Ice Dominion": {
+ "triggered": 49,
+ "displayed_kills": 11
+ },
+ "Snow's Grace": {
+ "triggered": 10,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Alonso": {
+ "Poison Harpoon": {
+ "triggered": 48,
+ "displayed_kills": 17
+ },
+ "Iron Strength": {
+ "triggered": 17,
+ "displayed_kills": null
+ },
+ "Onslaught": {
+ "triggered": 9,
+ "displayed_kills": null
+ }
+ },
+ "Natalia": {
+ "Feral Protection": {
+ "triggered": 8,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-29T16:05:07.000Z",
+ "randomseed": 1790697908,
+ "attacker": 1494,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171534441",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T16-05-32.103Z-94f7eb15-c2d4-4e42-a97a-c18ee930aac2.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1750,
+ "survivors": 1494,
+ "losses": 0,
+ "injured": 90,
+ "lightly_injured": 166,
+ "kills": 140
+ },
+ "defender": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 90
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 800,
+ "survivors": 544,
+ "losses": 0,
+ "injured": 90,
+ "lightly_injured": 166,
+ "kills": 20
+ },
+ "lancer_t6": {
+ "troops": 250,
+ "survivors": 250,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 36
+ },
+ "marksman_t6": {
+ "troops": 700,
+ "survivors": 700,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 84
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 9
+ },
+ "lancer_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 31
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 50
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Molly": {
+ "Ice Dominion": {
+ "triggered": 39,
+ "displayed_kills": 9
+ },
+ "Snow's Grace": {
+ "triggered": 10,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Alonso": {
+ "Poison Harpoon": {
+ "triggered": 33,
+ "displayed_kills": 13
+ },
+ "Iron Strength": {
+ "triggered": 20,
+ "displayed_kills": null
+ },
+ "Onslaught": {
+ "triggered": 12,
+ "displayed_kills": null
+ }
+ },
+ "Natalia": {
+ "Feral Protection": {
+ "triggered": 14,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-29T16:06:11.000Z",
+ "randomseed": 1790697972,
+ "attacker": 1477,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171534596",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T16-06-36.132Z-5c0c2252-b430-4009-9eda-82a55330fbdf.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1750,
+ "survivors": 1477,
+ "losses": 0,
+ "injured": 96,
+ "lightly_injured": 177,
+ "kills": 140
+ },
+ "defender": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 96
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 800,
+ "survivors": 527,
+ "losses": 0,
+ "injured": 96,
+ "lightly_injured": 177,
+ "kills": 20
+ },
+ "lancer_t6": {
+ "troops": 250,
+ "survivors": 250,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 36
+ },
+ "marksman_t6": {
+ "troops": 700,
+ "survivors": 700,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 84
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 9
+ },
+ "lancer_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 30
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 57
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Molly": {
+ "Ice Dominion": {
+ "triggered": 36,
+ "displayed_kills": 8
+ },
+ "Snow's Grace": {
+ "triggered": 8,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Alonso": {
+ "Poison Harpoon": {
+ "triggered": 42,
+ "displayed_kills": 17
+ },
+ "Iron Strength": {
+ "triggered": 14,
+ "displayed_kills": null
+ },
+ "Onslaught": {
+ "triggered": 12,
+ "displayed_kills": null
+ }
+ },
+ "Natalia": {
+ "Feral Protection": {
+ "triggered": 18,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-29T16:07:15.000Z",
+ "randomseed": 1790698036,
+ "attacker": 1509,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171534697",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T16-07-40.111Z-89121569-7701-4ccb-a754-d9d92b5801ed.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1750,
+ "survivors": 1509,
+ "losses": 0,
+ "injured": 85,
+ "lightly_injured": 156,
+ "kills": 140
+ },
+ "defender": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 85
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 800,
+ "survivors": 559,
+ "losses": 0,
+ "injured": 85,
+ "lightly_injured": 156,
+ "kills": 22
+ },
+ "lancer_t6": {
+ "troops": 250,
+ "survivors": 250,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 37
+ },
+ "marksman_t6": {
+ "troops": 700,
+ "survivors": 700,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 81
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 9
+ },
+ "lancer_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 27
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 49
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Molly": {
+ "Ice Dominion": {
+ "triggered": 47,
+ "displayed_kills": 14
+ },
+ "Snow's Grace": {
+ "triggered": 13,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Alonso": {
+ "Poison Harpoon": {
+ "triggered": 38,
+ "displayed_kills": 12
+ },
+ "Iron Strength": {
+ "triggered": 11,
+ "displayed_kills": null
+ },
+ "Onslaught": {
+ "triggered": 9,
+ "displayed_kills": null
+ }
+ },
+ "Natalia": {
+ "Feral Protection": {
+ "triggered": 14,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_benign_controls_20260929/renee_jasser_mixed.json
===================================================================
--- testcases/emulator_verified/renee_benign_controls_20260929/renee_jasser_mixed.json prev run
+++ testcases/emulator_verified/renee_benign_controls_20260929/renee_jasser_mixed.json this run
@@ -0,0 +1,157 @@
+[
+ {
+ "test_id": "renee_jasser_mixed",
+ "description": "C2 battle report 2734693171519542",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 100,
+ "lancer_t6": 300,
+ "marksman_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 297.4,
+ "defense": 286.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 250
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T04:21:07.000Z",
+ "randomseed": 1790655668,
+ "attacker": 485,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171519542",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T04-21-32.177Z-9fdf000c-3acd-496a-b7f4-1c2567c31d12.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 500,
+ "survivors": 485,
+ "losses": 0,
+ "injured": 6,
+ "lightly_injured": 9,
+ "kills": 88
+ },
+ "defender": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 6
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 100,
+ "survivors": 85,
+ "losses": 0,
+ "injured": 6,
+ "lightly_injured": 9,
+ "kills": 5
+ },
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 300,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 66
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 100,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 17
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 6
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 3,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_benign_controls_20260929/renee_jasser_pure.json
===================================================================
--- testcases/emulator_verified/renee_benign_controls_20260929/renee_jasser_pure.json prev run
+++ testcases/emulator_verified/renee_benign_controls_20260929/renee_jasser_pure.json this run
@@ -0,0 +1,139 @@
+[
+ {
+ "test_id": "renee_jasser_pure",
+ "description": "C2 battle report 2734693171519423",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "lancer_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 297.4,
+ "defense": 286.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 150
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T04:17:55.000Z",
+ "randomseed": 1790655476,
+ "attacker": 285,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171519423",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T04-18-20.145Z-5fa2ee37-9f16-435a-bad0-cb0a6df74622.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 300,
+ "survivors": 285,
+ "losses": 0,
+ "injured": 6,
+ "lightly_injured": 9,
+ "kills": 53
+ },
+ "defender": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 6
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 285,
+ "losses": 0,
+ "injured": 6,
+ "lightly_injured": 9,
+ "kills": 53
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 6
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 3,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_benign_controls_20260929/renee_sergey_mixed.json
===================================================================
--- testcases/emulator_verified/renee_benign_controls_20260929/renee_sergey_mixed.json prev run
+++ testcases/emulator_verified/renee_benign_controls_20260929/renee_sergey_mixed.json this run
@@ -0,0 +1,157 @@
+[
+ {
+ "test_id": "renee_sergey_mixed",
+ "description": "C2 battle report 2734693171519591",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Sergey": {
+ "skill_1": 2,
+ "skill_2": 2
+ }
+ },
+ "troops": {
+ "infantry_t6": 100,
+ "lancer_t6": 300,
+ "marksman_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 346.14,
+ "defense": 338.69,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 250
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T04:22:11.000Z",
+ "randomseed": 1790655732,
+ "attacker": 489,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171519591",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T04-22-36.187Z-4c2d0893-258b-4984-a1b1-0bf9ee2ebc6c.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 500,
+ "survivors": 489,
+ "losses": 0,
+ "injured": 4,
+ "lightly_injured": 7,
+ "kills": 88
+ },
+ "defender": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 4
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 100,
+ "survivors": 89,
+ "losses": 0,
+ "injured": 4,
+ "lightly_injured": 7,
+ "kills": 6
+ },
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 300,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 66
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 100,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 16
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 4
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 3,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_benign_controls_20260929/renee_sergey_pure.json
===================================================================
--- testcases/emulator_verified/renee_benign_controls_20260929/renee_sergey_pure.json prev run
+++ testcases/emulator_verified/renee_benign_controls_20260929/renee_sergey_pure.json this run
@@ -0,0 +1,139 @@
+[
+ {
+ "test_id": "renee_sergey_pure",
+ "description": "C2 battle report 2734693171519474",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Sergey": {
+ "skill_1": 2,
+ "skill_2": 2
+ }
+ },
+ "troops": {
+ "lancer_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 346.14,
+ "defense": 338.69,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 150
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T04:18:59.000Z",
+ "randomseed": 1790655540,
+ "attacker": 287,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171519474",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T04-19-24.149Z-ea29432b-6a52-4924-ac50-e06e631ce7d7.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 300,
+ "survivors": 287,
+ "losses": 0,
+ "injured": 5,
+ "lightly_injured": 8,
+ "kills": 53
+ },
+ "defender": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 5
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 287,
+ "losses": 0,
+ "injured": 5,
+ "lightly_injured": 8,
+ "kills": 53
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 5
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 3,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_benign_controls_20260929/renee_solo_mixed.json
===================================================================
--- testcases/emulator_verified/renee_benign_controls_20260929/renee_solo_mixed.json prev run
+++ testcases/emulator_verified/renee_benign_controls_20260929/renee_solo_mixed.json this run
@@ -0,0 +1,153 @@
+[
+ {
+ "test_id": "renee_solo_mixed",
+ "description": "C2 battle report 2734693171519498",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 100,
+ "lancer_t6": 300,
+ "marksman_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 250
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T04:20:03.000Z",
+ "randomseed": 1790655604,
+ "attacker": 484,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171519498",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T04-20-28.161Z-ba28cbf5-5cfb-4f25-a9f8-7c2782050b65.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 500,
+ "survivors": 484,
+ "losses": 0,
+ "injured": 6,
+ "lightly_injured": 10,
+ "kills": 88
+ },
+ "defender": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 6
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 100,
+ "survivors": 84,
+ "losses": 0,
+ "injured": 6,
+ "lightly_injured": 10,
+ "kills": 4
+ },
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 300,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 67
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 100,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 17
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 6
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 4,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_benign_controls_20260929/renee_solo_pure.json
===================================================================
--- testcases/emulator_verified/renee_benign_controls_20260929/renee_solo_pure.json prev run
+++ testcases/emulator_verified/renee_benign_controls_20260929/renee_solo_pure.json this run
@@ -0,0 +1,135 @@
+[
+ {
+ "test_id": "renee_solo_pure",
+ "description": "C2 battle report 2734693171519405",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 150
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T04:16:42.000Z",
+ "randomseed": 1790655403,
+ "attacker": 285,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171519405",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T04-17-07.191Z-9138f3a3-7630-449d-a9dd-e1c2a7a91ab7.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 300,
+ "survivors": 285,
+ "losses": 0,
+ "injured": 6,
+ "lightly_injured": 9,
+ "kills": 53
+ },
+ "defender": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 6
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 285,
+ "losses": 0,
+ "injured": 6,
+ "lightly_injured": 9,
+ "kills": 53
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 6
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 3,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_gwen_lancer_isolation_20260927/no_infantry_vs_infantry.json
===================================================================
--- testcases/emulator_verified/renee_gwen_lancer_isolation_20260927/no_infantry_vs_infantry.json prev run
+++ testcases/emulator_verified/renee_gwen_lancer_isolation_20260927/no_infantry_vs_infantry.json this run
@@ -0,0 +1,160 @@
+[
+ {
+ "test_id": "no_infantry_vs_infantry",
+ "description": "C2 battle report 2734693171422654",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 50,
+ "marksman_t6": 200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 1446
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-27T02:12:14.000Z",
+ "randomseed": 1790475135,
+ "attacker": 0,
+ "defender": 966,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171422654",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-27T02-12-29.673Z-312f5bf4-5b0a-48d1-b81b-760c51e3f572.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 168
+ },
+ "defender": {
+ "troops": 1446,
+ "survivors": 966,
+ "losses": 0,
+ "injured": 168,
+ "lightly_injured": 312,
+ "kills": 88
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 21
+ },
+ "marksman_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 147
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1446,
+ "survivors": 966,
+ "losses": 0,
+ "injured": 168,
+ "lightly_injured": 312,
+ "kills": 88
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 14,
+ "displayed_kills": 3
+ },
+ "Blastmaster": {
+ "triggered": 14,
+ "displayed_kills": 2
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 35,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_gwen_lancer_isolation_20260927/no_infantry_vs_lancer.json
===================================================================
--- testcases/emulator_verified/renee_gwen_lancer_isolation_20260927/no_infantry_vs_lancer.json prev run
+++ testcases/emulator_verified/renee_gwen_lancer_isolation_20260927/no_infantry_vs_lancer.json this run
@@ -0,0 +1,160 @@
+[
+ {
+ "test_id": "no_infantry_vs_lancer",
+ "description": "C2 battle report 2734693171422818",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 200,
+ "marksman_t6": 50
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 2567
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-27T02:14:30.000Z",
+ "randomseed": 1790475271,
+ "attacker": 0,
+ "defender": 1347,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171422818",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-27T02-14-45.797Z-75a31864-11a5-494d-aeef-9c528b96e6e9.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 427
+ },
+ "defender": {
+ "troops": 2567,
+ "survivors": 1347,
+ "losses": 0,
+ "injured": 427,
+ "lightly_injured": 793,
+ "kills": 88
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 251
+ },
+ "marksman_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 176
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 2567,
+ "survivors": 1347,
+ "losses": 0,
+ "injured": 427,
+ "lightly_injured": 793,
+ "kills": 88
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 9,
+ "displayed_kills": 5
+ },
+ "Blastmaster": {
+ "triggered": 6,
+ "displayed_kills": 2
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 15,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_gwen_lancer_isolation_20260927/no_marksmen_vs_infantry.json
===================================================================
--- testcases/emulator_verified/renee_gwen_lancer_isolation_20260927/no_marksmen_vs_infantry.json prev run
+++ testcases/emulator_verified/renee_gwen_lancer_isolation_20260927/no_marksmen_vs_infantry.json this run
@@ -0,0 +1,160 @@
+[
+ {
+ "test_id": "no_marksmen_vs_infantry",
+ "description": "C2 battle report 2734693171422553",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 50,
+ "lancer_t6": 50
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 1446
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-27T02:11:29.000Z",
+ "randomseed": 1790475090,
+ "attacker": 0,
+ "defender": 1077,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171422553",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-27T02-11-44.802Z-6355a0d9-4319-40ec-a219-c23e1d98fc62.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 36,
+ "lightly_injured": 64,
+ "kills": 130
+ },
+ "defender": {
+ "troops": 1446,
+ "survivors": 1077,
+ "losses": 0,
+ "injured": 130,
+ "lightly_injured": 239,
+ "kills": 36
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 6
+ },
+ "lancer_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 124
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1446,
+ "survivors": 1077,
+ "losses": 0,
+ "injured": 130,
+ "lightly_injured": 239,
+ "kills": 36
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 23,
+ "displayed_kills": 1
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 43,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_gwen_lancer_isolation_20260927/no_marksmen_vs_lancer.json
===================================================================
--- testcases/emulator_verified/renee_gwen_lancer_isolation_20260927/no_marksmen_vs_lancer.json prev run
+++ testcases/emulator_verified/renee_gwen_lancer_isolation_20260927/no_marksmen_vs_lancer.json this run
@@ -0,0 +1,160 @@
+[
+ {
+ "test_id": "no_marksmen_vs_lancer",
+ "description": "C2 battle report 2734693171422775",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 50,
+ "lancer_t6": 200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 2567
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-27T02:13:45.000Z",
+ "randomseed": 1790475226,
+ "attacker": 0,
+ "defender": 1065,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171422775",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-27T02-14-00.652Z-428012df-7f15-4bdd-aa28-7a33a7642391.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 526
+ },
+ "defender": {
+ "troops": 2567,
+ "survivors": 1065,
+ "losses": 0,
+ "injured": 526,
+ "lightly_injured": 976,
+ "kills": 88
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 9
+ },
+ "lancer_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 517
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 2567,
+ "survivors": 1065,
+ "losses": 0,
+ "injured": 526,
+ "lightly_injured": 976,
+ "kills": 88
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 7,
+ "displayed_kills": 6
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 20,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_gwen_lancer_isolation_20260927/only_lancers_vs_infantry.json
===================================================================
--- testcases/emulator_verified/renee_gwen_lancer_isolation_20260927/only_lancers_vs_infantry.json prev run
+++ testcases/emulator_verified/renee_gwen_lancer_isolation_20260927/only_lancers_vs_infantry.json this run
@@ -0,0 +1,151 @@
+[
+ {
+ "test_id": "only_lancers_vs_infantry",
+ "description": "C2 battle report 2734693171422497",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 50
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 1446
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-27T02:10:44.000Z",
+ "randomseed": 1790475045,
+ "attacker": 0,
+ "defender": 1360,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171422497",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-27T02-10-59.679Z-9036b338-46ac-4012-80c6-7008b31382d7.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 31
+ },
+ "defender": {
+ "troops": 1446,
+ "survivors": 1360,
+ "losses": 0,
+ "injured": 31,
+ "lightly_injured": 55,
+ "kills": 18
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 31
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1446,
+ "survivors": 1360,
+ "losses": 0,
+ "injured": 31,
+ "lightly_injured": 55,
+ "kills": 18
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 6,
+ "displayed_kills": null
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 20,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_gwen_lancer_isolation_20260927/only_lancers_vs_lancer.json
===================================================================
--- testcases/emulator_verified/renee_gwen_lancer_isolation_20260927/only_lancers_vs_lancer.json prev run
+++ testcases/emulator_verified/renee_gwen_lancer_isolation_20260927/only_lancers_vs_lancer.json this run
@@ -0,0 +1,151 @@
+[
+ {
+ "test_id": "only_lancers_vs_lancer",
+ "description": "C2 battle report 2734693171422716",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 2567
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-27T02:13:00.000Z",
+ "randomseed": 1790475181,
+ "attacker": 0,
+ "defender": 1759,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171422716",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-27T02-13-15.818Z-5522d97c-9e3c-412e-b640-317d5dab5998.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 283
+ },
+ "defender": {
+ "troops": 2567,
+ "survivors": 1759,
+ "losses": 0,
+ "injured": 283,
+ "lightly_injured": 525,
+ "kills": 70
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 283
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 2567,
+ "survivors": 1759,
+ "losses": 0,
+ "injured": 283,
+ "lightly_injured": 525,
+ "kills": 70
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 4,
+ "displayed_kills": 3
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 14,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_gwen_no_lancers_20260927/no_lancers_1.json
===================================================================
--- testcases/emulator_verified/renee_gwen_no_lancers_20260927/no_lancers_1.json prev run
+++ testcases/emulator_verified/renee_gwen_no_lancers_20260927/no_lancers_1.json this run
@@ -0,0 +1,154 @@
+[
+ {
+ "test_id": "no_lancers_1",
+ "description": "C2 battle report 2734693171421999",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 50,
+ "marksman_t6": 200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 1446
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-27T02:03:24.000Z",
+ "randomseed": 1790474605,
+ "attacker": 0,
+ "defender": 968,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171421999",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-27T02-03-39.807Z-3cd3c21b-2464-42f5-9178-4f8bed6bf571.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 168
+ },
+ "defender": {
+ "troops": 1446,
+ "survivors": 968,
+ "losses": 0,
+ "injured": 168,
+ "lightly_injured": 310,
+ "kills": 88
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 3
+ },
+ "marksman_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 165
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1446,
+ "survivors": 968,
+ "losses": 0,
+ "injured": 168,
+ "lightly_injured": 310,
+ "kills": 88
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 19,
+ "displayed_kills": 4
+ },
+ "Blastmaster": {
+ "triggered": 17,
+ "displayed_kills": 1
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_gwen_no_lancers_20260927/no_lancers_2.json
===================================================================
--- testcases/emulator_verified/renee_gwen_no_lancers_20260927/no_lancers_2.json prev run
+++ testcases/emulator_verified/renee_gwen_no_lancers_20260927/no_lancers_2.json this run
@@ -0,0 +1,154 @@
+[
+ {
+ "test_id": "no_lancers_2",
+ "description": "C2 battle report 2734693171422031",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 50,
+ "marksman_t6": 50
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 2567
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-27T02:04:09.000Z",
+ "randomseed": 1790474650,
+ "attacker": 0,
+ "defender": 2424,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171422031",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-27T02-04-24.719Z-6e61cd10-b21e-403a-ab66-85db070e2c80.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 36,
+ "lightly_injured": 64,
+ "kills": 51
+ },
+ "defender": {
+ "troops": 2567,
+ "survivors": 2424,
+ "losses": 0,
+ "injured": 51,
+ "lightly_injured": 92,
+ "kills": 36
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 6
+ },
+ "marksman_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 45
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 2567,
+ "survivors": 2424,
+ "losses": 0,
+ "injured": 51,
+ "lightly_injured": 92,
+ "kills": 36
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 5,
+ "displayed_kills": 1
+ },
+ "Blastmaster": {
+ "triggered": 4,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_gwen_no_lancers_20260927/no_lancers_3.json
===================================================================
--- testcases/emulator_verified/renee_gwen_no_lancers_20260927/no_lancers_3.json prev run
+++ testcases/emulator_verified/renee_gwen_no_lancers_20260927/no_lancers_3.json this run
@@ -0,0 +1,154 @@
+[
+ {
+ "test_id": "no_lancers_3",
+ "description": "C2 battle report 2734693171422073",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 100,
+ "marksman_t6": 200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 600
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-27T02:04:54.000Z",
+ "randomseed": 1790474695,
+ "attacker": 257,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171422073",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-27T02-05-09.781Z-a25aa0a6-5c6c-42de-a559-41ca3943b8ff.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 300,
+ "survivors": 257,
+ "losses": 0,
+ "injured": 16,
+ "lightly_injured": 27,
+ "kills": 210
+ },
+ "defender": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 16
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 100,
+ "survivors": 57,
+ "losses": 0,
+ "injured": 16,
+ "lightly_injured": 27,
+ "kills": 24
+ },
+ "marksman_t6": {
+ "troops": 200,
+ "survivors": 200,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 186
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 16
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 6,
+ "displayed_kills": 5
+ },
+ "Blastmaster": {
+ "triggered": 4,
+ "displayed_kills": 3
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_gwen_no_lancers_20260927/no_lancers_4.json
===================================================================
--- testcases/emulator_verified/renee_gwen_no_lancers_20260927/no_lancers_4.json prev run
+++ testcases/emulator_verified/renee_gwen_no_lancers_20260927/no_lancers_4.json this run
@@ -0,0 +1,154 @@
+[
+ {
+ "test_id": "no_lancers_4",
+ "description": "C2 battle report 2734693171422164",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 200,
+ "marksman_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-27T02:05:39.000Z",
+ "randomseed": 1790474740,
+ "attacker": 183,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171422164",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-27T02-05-54.731Z-ebbe6aef-0ca7-4ea5-8259-ada9eacee89f.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 300,
+ "survivors": 183,
+ "losses": 0,
+ "injured": 41,
+ "lightly_injured": 76,
+ "kills": 350
+ },
+ "defender": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 41
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 83,
+ "losses": 0,
+ "injured": 41,
+ "lightly_injured": 76,
+ "kills": 66
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 100,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 284
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 41
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 16,
+ "displayed_kills": 10
+ },
+ "Blastmaster": {
+ "triggered": 9,
+ "displayed_kills": 4
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_gwen_precision_20260925/gwen_only_1000l_vs_3000l.json
===================================================================
--- testcases/emulator_verified/renee_gwen_precision_20260925/gwen_only_1000l_vs_3000l.json prev run
+++ testcases/emulator_verified/renee_gwen_precision_20260925/gwen_only_1000l_vs_3000l.json this run
@@ -0,0 +1,144 @@
+[
+ {
+ "test_id": "gwen_only_1000l_vs_3000l",
+ "description": "C2 battle report 2734693171346281",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 435.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "lancer_t6": 3000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 297.4,
+ "defense": 286.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T10:06:29.000Z",
+ "randomseed": 1790330790,
+ "attacker": 0,
+ "defender": 2086,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171346281",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T10-06-44.984Z-a57688bb-e91a-40b5-812e-c3c28f054519.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 320
+ },
+ "defender": {
+ "troops": 3000,
+ "survivors": 2086,
+ "losses": 0,
+ "injured": 320,
+ "lightly_injured": 594,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 320
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 3000,
+ "survivors": 2086,
+ "losses": 0,
+ "injured": 320,
+ "lightly_injured": 594,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 4,
+ "displayed_kills": 9
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_gwen_precision_20260925/renee_gwen_1000l_vs_7800l.json
===================================================================
--- testcases/emulator_verified/renee_gwen_precision_20260925/renee_gwen_1000l_vs_7800l.json prev run
+++ testcases/emulator_verified/renee_gwen_precision_20260925/renee_gwen_1000l_vs_7800l.json this run
@@ -0,0 +1,156 @@
+[
+ {
+ "test_id": "renee_gwen_1000l_vs_7800l",
+ "description": "C2 battle report 2734693171346231",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 607.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 435.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "lancer_t6": 7800
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 297.4,
+ "defense": 286.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T10:03:19.000Z",
+ "randomseed": 1790330600,
+ "attacker": 0,
+ "defender": 2518,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171346231",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T10-03-34.975Z-facc20ac-e60b-46f7-93e1-de1c59b44cd7.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 1849
+ },
+ "defender": {
+ "troops": 7800,
+ "survivors": 2518,
+ "losses": 0,
+ "injured": 1849,
+ "lightly_injured": 3433,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 1849
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 7800,
+ "survivors": 2518,
+ "losses": 0,
+ "injured": 1849,
+ "lightly_injured": 3433,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 6,
+ "displayed_kills": 23
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 19,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_gwen_precision_20260925/renee_only_1000l_vs_7800l.json
===================================================================
--- testcases/emulator_verified/renee_gwen_precision_20260925/renee_only_1000l_vs_7800l.json prev run
+++ testcases/emulator_verified/renee_gwen_precision_20260925/renee_only_1000l_vs_7800l.json this run
@@ -0,0 +1,140 @@
+[
+ {
+ "test_id": "renee_only_1000l_vs_7800l",
+ "description": "C2 battle report 2734693171346273",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 607.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "lancer_t6": 7800
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 297.4,
+ "defense": 286.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T10:05:02.000Z",
+ "randomseed": 1790330703,
+ "attacker": 0,
+ "defender": 3037,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171346273",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T10-05-17.976Z-bd2634fc-cf60-4ee6-8c7c-f6babfff7ecd.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 1668
+ },
+ "defender": {
+ "troops": 7800,
+ "survivors": 3037,
+ "losses": 0,
+ "injured": 1668,
+ "lightly_injured": 3095,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 1668
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 7800,
+ "survivors": 3037,
+ "losses": 0,
+ "injured": 1668,
+ "lightly_injured": 3095,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 18,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_gwen_single_lines_20260927/only_infantry.json
===================================================================
--- testcases/emulator_verified/renee_gwen_single_lines_20260927/only_infantry.json prev run
+++ testcases/emulator_verified/renee_gwen_single_lines_20260927/only_infantry.json this run
@@ -0,0 +1,145 @@
+[
+ {
+ "test_id": "only_infantry",
+ "description": "C2 battle report 2734693171424617",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-27T02:37:03.000Z",
+ "randomseed": 1790476624,
+ "attacker": 0,
+ "defender": 807,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171424617",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-27T02-37-18.528Z-fc7a57db-3a7a-4a38-baf0-8dfd06b49069.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 68
+ },
+ "defender": {
+ "troops": 1000,
+ "survivors": 807,
+ "losses": 0,
+ "injured": 68,
+ "lightly_injured": 125,
+ "kills": 70
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 68
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 807,
+ "losses": 0,
+ "injured": 68,
+ "lightly_injured": 125,
+ "kills": 70
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 12,
+ "displayed_kills": 2
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_gwen_single_lines_20260927/only_marksman.json
===================================================================
--- testcases/emulator_verified/renee_gwen_single_lines_20260927/only_marksman.json prev run
+++ testcases/emulator_verified/renee_gwen_single_lines_20260927/only_marksman.json this run
@@ -0,0 +1,145 @@
+[
+ {
+ "test_id": "only_marksman",
+ "description": "C2 battle report 2734693171424687",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "marksman_t6": 200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-27T02:37:48.000Z",
+ "randomseed": 1790476669,
+ "attacker": 0,
+ "defender": 709,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171424687",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-27T02-38-03.813Z-54044085-80ab-48a3-98ae-2ca585e3c406.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 102
+ },
+ "defender": {
+ "troops": 1000,
+ "survivors": 709,
+ "losses": 0,
+ "injured": 102,
+ "lightly_injured": 189,
+ "kills": 70
+ },
+ "troop_details": {
+ "attacker": {
+ "marksman_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 102
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 709,
+ "losses": 0,
+ "injured": 102,
+ "lightly_injured": 189,
+ "kills": 70
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 3,
+ "displayed_kills": 3
+ },
+ "Blastmaster": {
+ "triggered": 4,
+ "displayed_kills": 2
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_gwen_t9_probe_20260929/g_1000l_vs_20000t9.json
===================================================================
--- testcases/emulator_verified/renee_gwen_t9_probe_20260929/g_1000l_vs_20000t9.json prev run
+++ testcases/emulator_verified/renee_gwen_t9_probe_20260929/g_1000l_vs_20000t9.json this run
@@ -0,0 +1,146 @@
+[
+ {
+ "test_id": "g_1000l_vs_20000t9",
+ "description": "C2 battle report 2734693171525691",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 435.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t9": 20000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T10:11:07.000Z",
+ "randomseed": 1790676668,
+ "attacker": 0,
+ "defender": 19858,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171525691",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T10-11-23.120Z-4ba489d1-8874-42c0-86e3-11848a2cdc12.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 50
+ },
+ "defender": {
+ "troops": 20000,
+ "survivors": 19858,
+ "losses": 0,
+ "injured": 50,
+ "lightly_injured": 92,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 50
+ }
+ },
+ "defender": {
+ "lancer_t9": {
+ "troops": 20000,
+ "survivors": 19858,
+ "losses": 0,
+ "injured": 50,
+ "lightly_injured": 92,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 1,
+ "displayed_kills": 1
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Troop skills": {
+ "Ambusher": {
+ "triggered": 2,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_gwen_t9_probe_20260929/g_60i_1000l_vs_20000t9.json
===================================================================
--- testcases/emulator_verified/renee_gwen_t9_probe_20260929/g_60i_1000l_vs_20000t9.json prev run
+++ testcases/emulator_verified/renee_gwen_t9_probe_20260929/g_60i_1000l_vs_20000t9.json this run
@@ -0,0 +1,155 @@
+[
+ {
+ "test_id": "g_60i_1000l_vs_20000t9",
+ "description": "C2 battle report 2734693171525906",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 60,
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 435.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t9": 20000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T10:25:23.000Z",
+ "randomseed": 1790677524,
+ "attacker": 0,
+ "defender": 19787,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171525906",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T10-25-39.108Z-d2a91b29-0882-4581-97de-82057530a9b4.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1060,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 371,
+ "lightly_injured": 689,
+ "kills": 75
+ },
+ "defender": {
+ "troops": 20000,
+ "survivors": 19787,
+ "losses": 0,
+ "injured": 75,
+ "lightly_injured": 138,
+ "kills": 371
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 60,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 21,
+ "lightly_injured": 39,
+ "kills": 1
+ },
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 74
+ }
+ },
+ "defender": {
+ "lancer_t9": {
+ "troops": 20000,
+ "survivors": 19787,
+ "losses": 0,
+ "injured": 75,
+ "lightly_injured": 138,
+ "kills": 371
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 1,
+ "displayed_kills": 1
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Troop skills": {
+ "Ambusher": {
+ "triggered": 2,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_gwen_t9_probe_20260929/r_1000l_vs_20000t9.json
===================================================================
--- testcases/emulator_verified/renee_gwen_t9_probe_20260929/r_1000l_vs_20000t9.json prev run
+++ testcases/emulator_verified/renee_gwen_t9_probe_20260929/r_1000l_vs_20000t9.json this run
@@ -0,0 +1,142 @@
+[
+ {
+ "test_id": "r_1000l_vs_20000t9",
+ "description": "C2 battle report 2734693171525676",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 607.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t9": 20000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T10:10:21.000Z",
+ "randomseed": 1790676622,
+ "attacker": 0,
+ "defender": 18980,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171525676",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T10-10-37.107Z-66f60609-c560-4577-907d-0401d3f64e08.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 357
+ },
+ "defender": {
+ "troops": 20000,
+ "survivors": 18980,
+ "losses": 0,
+ "injured": 357,
+ "lightly_injured": 663,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 357
+ }
+ },
+ "defender": {
+ "lancer_t9": {
+ "troops": 20000,
+ "survivors": 18980,
+ "losses": 0,
+ "injured": 357,
+ "lightly_injured": 663,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 6,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Troop skills": {
+ "Ambusher": {
+ "triggered": 4,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_gwen_t9_probe_20260929/r_60i_1000l_vs_20000t9.json
===================================================================
--- testcases/emulator_verified/renee_gwen_t9_probe_20260929/r_60i_1000l_vs_20000t9.json prev run
+++ testcases/emulator_verified/renee_gwen_t9_probe_20260929/r_60i_1000l_vs_20000t9.json this run
@@ -0,0 +1,151 @@
+[
+ {
+ "test_id": "r_60i_1000l_vs_20000t9",
+ "description": "C2 battle report 2734693171525899",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 60,
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 607.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t9": 20000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T10:24:37.000Z",
+ "randomseed": 1790677478,
+ "attacker": 0,
+ "defender": 18729,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171525899",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T10-24-53.111Z-cc396bcd-837b-457b-96d6-7c2bfe3a7a1e.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1060,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 371,
+ "lightly_injured": 689,
+ "kills": 445
+ },
+ "defender": {
+ "troops": 20000,
+ "survivors": 18729,
+ "losses": 0,
+ "injured": 445,
+ "lightly_injured": 826,
+ "kills": 371
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 60,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 21,
+ "lightly_injured": 39,
+ "kills": 1
+ },
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 444
+ }
+ },
+ "defender": {
+ "lancer_t9": {
+ "troops": 20000,
+ "survivors": 18729,
+ "losses": 0,
+ "injured": 445,
+ "lightly_injured": 826,
+ "kills": 371
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 7,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Troop skills": {
+ "Ambusher": {
+ "triggered": 1,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_gwen_t9_probe_20260929/rg_1000l_vs_20000t9.json
===================================================================
--- testcases/emulator_verified/renee_gwen_t9_probe_20260929/rg_1000l_vs_20000t9.json prev run
+++ testcases/emulator_verified/renee_gwen_t9_probe_20260929/rg_1000l_vs_20000t9.json this run
@@ -0,0 +1,158 @@
+[
+ {
+ "test_id": "rg_1000l_vs_20000t9",
+ "description": "C2 battle report 2734693171525646",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 607.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 435.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t9": 20000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T10:09:10.000Z",
+ "randomseed": 1790676551,
+ "attacker": 0,
+ "defender": 18899,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171525646",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T10-09-26.158Z-6ec73045-b76a-49a7-93e3-12e4a36d45c2.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 386
+ },
+ "defender": {
+ "troops": 20000,
+ "survivors": 18899,
+ "losses": 0,
+ "injured": 386,
+ "lightly_injured": 715,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 386
+ }
+ },
+ "defender": {
+ "lancer_t9": {
+ "troops": 20000,
+ "survivors": 18899,
+ "losses": 0,
+ "injured": 386,
+ "lightly_injured": 715,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 2,
+ "displayed_kills": 3
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 6,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Troop skills": {
+ "Ambusher": {
+ "triggered": 4,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_gwen_t9_probe_20260929/rg_200i_1000l_vs_20000t9.json
===================================================================
--- testcases/emulator_verified/renee_gwen_t9_probe_20260929/rg_200i_1000l_vs_20000t9.json prev run
+++ testcases/emulator_verified/renee_gwen_t9_probe_20260929/rg_200i_1000l_vs_20000t9.json this run
@@ -0,0 +1,167 @@
+[
+ {
+ "test_id": "rg_200i_1000l_vs_20000t9",
+ "description": "C2 battle report 2734693171525704",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 200,
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 607.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 435.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t9": 20000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T10:13:24.000Z",
+ "randomseed": 1790676805,
+ "attacker": 0,
+ "defender": 18241,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171525704",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T10-13-40.113Z-6e8310a7-e3ea-4e34-b721-de96368f2715.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 420,
+ "lightly_injured": 780,
+ "kills": 616
+ },
+ "defender": {
+ "troops": 20000,
+ "survivors": 18241,
+ "losses": 0,
+ "injured": 616,
+ "lightly_injured": 1143,
+ "kills": 420
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 8
+ },
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 608
+ }
+ },
+ "defender": {
+ "lancer_t9": {
+ "troops": 20000,
+ "survivors": 18241,
+ "losses": 0,
+ "injured": 616,
+ "lightly_injured": 1143,
+ "kills": 420
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 2,
+ "displayed_kills": 7
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 8,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Troop skills": {
+ "Ambusher": {
+ "triggered": 4,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_gwen_t9_probe_20260929/rg_30i_1000l_vs_20000t9.json
===================================================================
--- testcases/emulator_verified/renee_gwen_t9_probe_20260929/rg_30i_1000l_vs_20000t9.json prev run
+++ testcases/emulator_verified/renee_gwen_t9_probe_20260929/rg_30i_1000l_vs_20000t9.json this run
@@ -0,0 +1,167 @@
+[
+ {
+ "test_id": "rg_30i_1000l_vs_20000t9",
+ "description": "C2 battle report 2734693171525840",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 30,
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 607.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 435.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t9": 20000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T10:21:38.000Z",
+ "randomseed": 1790677299,
+ "attacker": 0,
+ "defender": 18753,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171525840",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T10-21-54.121Z-f0d0ccf9-9ff2-493c-a21f-bf1d413c374d.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1030,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 361,
+ "lightly_injured": 669,
+ "kills": 437
+ },
+ "defender": {
+ "troops": 20000,
+ "survivors": 18753,
+ "losses": 0,
+ "injured": 437,
+ "lightly_injured": 810,
+ "kills": 361
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 30,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 11,
+ "lightly_injured": 19,
+ "kills": 0
+ },
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 437
+ }
+ },
+ "defender": {
+ "lancer_t9": {
+ "troops": 20000,
+ "survivors": 18753,
+ "losses": 0,
+ "injured": 437,
+ "lightly_injured": 810,
+ "kills": 361
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 2,
+ "displayed_kills": 4
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 6,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Troop skills": {
+ "Ambusher": {
+ "triggered": 3,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_gwen_t9_probe_20260929/rg_400i_1000l_vs_20000t9.json
===================================================================
--- testcases/emulator_verified/renee_gwen_t9_probe_20260929/rg_400i_1000l_vs_20000t9.json prev run
+++ testcases/emulator_verified/renee_gwen_t9_probe_20260929/rg_400i_1000l_vs_20000t9.json this run
@@ -0,0 +1,167 @@
+[
+ {
+ "test_id": "rg_400i_1000l_vs_20000t9",
+ "description": "C2 battle report 2734693171525878",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 400,
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 607.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 435.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t9": 20000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T10:23:09.000Z",
+ "randomseed": 1790677390,
+ "attacker": 0,
+ "defender": 17648,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171525878",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T10-23-25.124Z-c19dc68b-d494-4e3f-bde1-c1faf1e83d7f.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 490,
+ "lightly_injured": 910,
+ "kills": 824
+ },
+ "defender": {
+ "troops": 20000,
+ "survivors": 17648,
+ "losses": 0,
+ "injured": 824,
+ "lightly_injured": 1528,
+ "kills": 490
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 26
+ },
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 798
+ }
+ },
+ "defender": {
+ "lancer_t9": {
+ "troops": 20000,
+ "survivors": 17648,
+ "losses": 0,
+ "injured": 824,
+ "lightly_injured": 1528,
+ "kills": 490
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 4,
+ "displayed_kills": 9
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 9,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Troop skills": {
+ "Ambusher": {
+ "triggered": 4,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_gwen_t9_probe_20260929/rg_60i_1000l_vs_20000t9.json
===================================================================
--- testcases/emulator_verified/renee_gwen_t9_probe_20260929/rg_60i_1000l_vs_20000t9.json prev run
+++ testcases/emulator_verified/renee_gwen_t9_probe_20260929/rg_60i_1000l_vs_20000t9.json this run
@@ -0,0 +1,167 @@
+[
+ {
+ "test_id": "rg_60i_1000l_vs_20000t9",
+ "description": "C2 battle report 2734693171525850",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 60,
+ "lancer_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 607.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 435.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t9": 20000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T10:22:24.000Z",
+ "randomseed": 1790677345,
+ "attacker": 0,
+ "defender": 18616,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171525850",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T10-22-40.107Z-433709f2-68de-479f-9cc9-54856267f142.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1060,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 371,
+ "lightly_injured": 689,
+ "kills": 485
+ },
+ "defender": {
+ "troops": 20000,
+ "survivors": 18616,
+ "losses": 0,
+ "injured": 485,
+ "lightly_injured": 899,
+ "kills": 371
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 60,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 21,
+ "lightly_injured": 39,
+ "kills": 1
+ },
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 484
+ }
+ },
+ "defender": {
+ "lancer_t9": {
+ "troops": 20000,
+ "survivors": 18616,
+ "losses": 0,
+ "injured": 485,
+ "lightly_injured": 899,
+ "kills": 371
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 2,
+ "displayed_kills": 4
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 7,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Troop skills": {
+ "Ambusher": {
+ "triggered": 4,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_mixed_partners_20260929/renee_jasser_balanced.json
===================================================================
--- testcases/emulator_verified/renee_mixed_partners_20260929/renee_jasser_balanced.json prev run
+++ testcases/emulator_verified/renee_mixed_partners_20260929/renee_jasser_balanced.json this run
@@ -0,0 +1,175 @@
+[
+ {
+ "test_id": "renee_jasser_balanced",
+ "description": "C2 battle report 2734693171520002",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 150,
+ "lancer_t6": 200,
+ "marksman_t6": 150
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 297.4,
+ "defense": 286.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 200,
+ "lancer_t6": 500,
+ "marksman_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T04:33:59.000Z",
+ "randomseed": 1790656440,
+ "attacker": 0,
+ "defender": 432,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171520002",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T04-34-24.156Z-9b84bce7-870a-4e7c-8c36-7e6fdaab5913.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 176,
+ "lightly_injured": 324,
+ "kills": 199
+ },
+ "defender": {
+ "troops": 1000,
+ "survivors": 432,
+ "losses": 0,
+ "injured": 199,
+ "lightly_injured": 369,
+ "kills": 176
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 2
+ },
+ "lancer_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 110
+ },
+ "marksman_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 87
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 4
+ },
+ "lancer_t6": {
+ "troops": 500,
+ "survivors": 132,
+ "losses": 0,
+ "injured": 129,
+ "lightly_injured": 239,
+ "kills": 76
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 300,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 96
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 13,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_mixed_partners_20260929/renee_jasser_lancer_heavy.json
===================================================================
--- testcases/emulator_verified/renee_mixed_partners_20260929/renee_jasser_lancer_heavy.json prev run
+++ testcases/emulator_verified/renee_mixed_partners_20260929/renee_jasser_lancer_heavy.json this run
@@ -0,0 +1,175 @@
+[
+ {
+ "test_id": "renee_jasser_lancer_heavy",
+ "description": "C2 battle report 2734693171519840",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 50,
+ "lancer_t6": 300,
+ "marksman_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 297.4,
+ "defense": 286.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 300,
+ "lancer_t6": 600,
+ "marksman_t6": 200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T04:29:43.000Z",
+ "randomseed": 1790656184,
+ "attacker": 0,
+ "defender": 820,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171519840",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T04-30-08.194Z-a6e4ffdc-f86b-4f05-9ad3-2890b91bf95a.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 450,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 158,
+ "lightly_injured": 292,
+ "kills": 98
+ },
+ "defender": {
+ "troops": 1100,
+ "survivors": 820,
+ "losses": 0,
+ "injured": 98,
+ "lightly_injured": 182,
+ "kills": 158
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 0
+ },
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 64
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 34
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 300,
+ "survivors": 20,
+ "losses": 0,
+ "injured": 98,
+ "lightly_injured": 182,
+ "kills": 12
+ },
+ "lancer_t6": {
+ "troops": 600,
+ "survivors": 600,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 84
+ },
+ "marksman_t6": {
+ "troops": 200,
+ "survivors": 200,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 62
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 11,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_mixed_partners_20260929/renee_seoyoon_balanced.json
===================================================================
--- testcases/emulator_verified/renee_mixed_partners_20260929/renee_seoyoon_balanced.json prev run
+++ testcases/emulator_verified/renee_mixed_partners_20260929/renee_seoyoon_balanced.json this run
@@ -0,0 +1,175 @@
+[
+ {
+ "test_id": "renee_seoyoon_balanced",
+ "description": "C2 battle report 2734693171520041",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Seo-yoon": {
+ "skill_1": 4,
+ "skill_2": 3
+ }
+ },
+ "troops": {
+ "infantry_t6": 150,
+ "lancer_t6": 200,
+ "marksman_t6": 150
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 297.4,
+ "defense": 286.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 200,
+ "lancer_t6": 500,
+ "marksman_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T04:36:07.000Z",
+ "randomseed": 1790656568,
+ "attacker": 0,
+ "defender": 195,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171520041",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T04-36-32.181Z-ce0809fa-972d-4e6b-a14c-ec190b34a3f3.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 176,
+ "lightly_injured": 324,
+ "kills": 282
+ },
+ "defender": {
+ "troops": 1000,
+ "survivors": 195,
+ "losses": 0,
+ "injured": 282,
+ "lightly_injured": 523,
+ "kills": 176
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 3
+ },
+ "lancer_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 149
+ },
+ "marksman_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 130
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 3
+ },
+ "lancer_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 51
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 195,
+ "losses": 0,
+ "injured": 37,
+ "lightly_injured": 68,
+ "kills": 122
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 15,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_mixed_partners_20260929/renee_seoyoon_lancer_heavy.json
===================================================================
--- testcases/emulator_verified/renee_mixed_partners_20260929/renee_seoyoon_lancer_heavy.json prev run
+++ testcases/emulator_verified/renee_mixed_partners_20260929/renee_seoyoon_lancer_heavy.json this run
@@ -0,0 +1,175 @@
+[
+ {
+ "test_id": "renee_seoyoon_lancer_heavy",
+ "description": "C2 battle report 2734693171519981",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Seo-yoon": {
+ "skill_1": 4,
+ "skill_2": 3
+ }
+ },
+ "troops": {
+ "infantry_t6": 50,
+ "lancer_t6": 300,
+ "marksman_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 297.4,
+ "defense": 286.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 300,
+ "lancer_t6": 600,
+ "marksman_t6": 200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T04:31:51.000Z",
+ "randomseed": 1790656312,
+ "attacker": 0,
+ "defender": 791,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171519981",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T04-32-16.147Z-de5932bd-6442-4d82-a8fb-5c95fc39c57c.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 450,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 158,
+ "lightly_injured": 292,
+ "kills": 109
+ },
+ "defender": {
+ "troops": 1100,
+ "survivors": 791,
+ "losses": 0,
+ "injured": 109,
+ "lightly_injured": 200,
+ "kills": 158
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 0
+ },
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 71
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 38
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 11
+ },
+ "lancer_t6": {
+ "troops": 600,
+ "survivors": 591,
+ "losses": 0,
+ "injured": 4,
+ "lightly_injured": 5,
+ "kills": 86
+ },
+ "marksman_t6": {
+ "troops": 200,
+ "survivors": 200,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 61
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 11,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_mixed_partners_20260929/renee_sergey_balanced.json
===================================================================
--- testcases/emulator_verified/renee_mixed_partners_20260929/renee_sergey_balanced.json prev run
+++ testcases/emulator_verified/renee_mixed_partners_20260929/renee_sergey_balanced.json this run
@@ -0,0 +1,175 @@
+[
+ {
+ "test_id": "renee_sergey_balanced",
+ "description": "C2 battle report 2734693171520015",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Sergey": {
+ "skill_1": 2,
+ "skill_2": 2
+ }
+ },
+ "troops": {
+ "infantry_t6": 150,
+ "lancer_t6": 200,
+ "marksman_t6": 150
+ },
+ "stats": {
+ "infantry": {
+ "attack": 346.14,
+ "defense": 338.69,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 200,
+ "lancer_t6": 500,
+ "marksman_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T04:35:03.000Z",
+ "randomseed": 1790656504,
+ "attacker": 0,
+ "defender": 161,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171520015",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T04-35-28.159Z-6921bd5b-83ea-42c9-b7be-380dbab4abb3.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 176,
+ "lightly_injured": 324,
+ "kills": 294
+ },
+ "defender": {
+ "troops": 1000,
+ "survivors": 161,
+ "losses": 0,
+ "injured": 294,
+ "lightly_injured": 545,
+ "kills": 176
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 5
+ },
+ "lancer_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 172
+ },
+ "marksman_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 117
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 2
+ },
+ "lancer_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 46
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 161,
+ "losses": 0,
+ "injured": 49,
+ "lightly_injured": 90,
+ "kills": 128
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 20,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_mixed_partners_20260929/renee_sergey_lancer_heavy.json
===================================================================
--- testcases/emulator_verified/renee_mixed_partners_20260929/renee_sergey_lancer_heavy.json prev run
+++ testcases/emulator_verified/renee_mixed_partners_20260929/renee_sergey_lancer_heavy.json this run
@@ -0,0 +1,175 @@
+[
+ {
+ "test_id": "renee_sergey_lancer_heavy",
+ "description": "C2 battle report 2734693171519930",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Sergey": {
+ "skill_1": 2,
+ "skill_2": 2
+ }
+ },
+ "troops": {
+ "infantry_t6": 50,
+ "lancer_t6": 300,
+ "marksman_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 346.14,
+ "defense": 338.69,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 300,
+ "lancer_t6": 600,
+ "marksman_t6": 200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T04:30:47.000Z",
+ "randomseed": 1790656248,
+ "attacker": 0,
+ "defender": 788,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171519930",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T04-31-12.159Z-3be22590-39cd-48f1-9d8e-2c14f97b3c7d.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 450,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 158,
+ "lightly_injured": 292,
+ "kills": 110
+ },
+ "defender": {
+ "troops": 1100,
+ "survivors": 788,
+ "losses": 0,
+ "injured": 110,
+ "lightly_injured": 202,
+ "kills": 158
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 0
+ },
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 77
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 33
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 10
+ },
+ "lancer_t6": {
+ "troops": 600,
+ "survivors": 588,
+ "losses": 0,
+ "injured": 5,
+ "lightly_injured": 7,
+ "kills": 85
+ },
+ "marksman_t6": {
+ "troops": 200,
+ "survivors": 200,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 63
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 14,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_mixed_partners_20260929/renee_solo_balanced.json
===================================================================
--- testcases/emulator_verified/renee_mixed_partners_20260929/renee_solo_balanced.json prev run
+++ testcases/emulator_verified/renee_mixed_partners_20260929/renee_solo_balanced.json this run
@@ -0,0 +1,171 @@
+[
+ {
+ "test_id": "renee_solo_balanced",
+ "description": "C2 battle report 2734693171519985",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 150,
+ "lancer_t6": 200,
+ "marksman_t6": 150
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 200,
+ "lancer_t6": 500,
+ "marksman_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T04:32:55.000Z",
+ "randomseed": 1790656376,
+ "attacker": 0,
+ "defender": 584,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171519985",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T04-33-20.146Z-49f49567-2744-4c61-a38b-8f73ecf36b20.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 176,
+ "lightly_injured": 324,
+ "kills": 146
+ },
+ "defender": {
+ "troops": 1000,
+ "survivors": 584,
+ "losses": 0,
+ "injured": 146,
+ "lightly_injured": 270,
+ "kills": 176
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 2
+ },
+ "lancer_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 86
+ },
+ "marksman_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 58
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 5
+ },
+ "lancer_t6": {
+ "troops": 500,
+ "survivors": 284,
+ "losses": 0,
+ "injured": 76,
+ "lightly_injured": 140,
+ "kills": 82
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 300,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 89
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 12,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_mixed_partners_20260929/renee_solo_lancer_heavy.json
===================================================================
--- testcases/emulator_verified/renee_mixed_partners_20260929/renee_solo_lancer_heavy.json prev run
+++ testcases/emulator_verified/renee_mixed_partners_20260929/renee_solo_lancer_heavy.json this run
@@ -0,0 +1,171 @@
+[
+ {
+ "test_id": "renee_solo_lancer_heavy",
+ "description": "C2 battle report 2734693171519814",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 50,
+ "lancer_t6": 300,
+ "marksman_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 300,
+ "lancer_t6": 600,
+ "marksman_t6": 200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-29T04:28:39.000Z",
+ "randomseed": 1790656120,
+ "attacker": 0,
+ "defender": 859,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171519814",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-29T04-29-04.185Z-dfed11fd-ef2d-4b2d-9d72-019d22103658.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 450,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 158,
+ "lightly_injured": 292,
+ "kills": 85
+ },
+ "defender": {
+ "troops": 1100,
+ "survivors": 859,
+ "losses": 0,
+ "injured": 85,
+ "lightly_injured": 156,
+ "kills": 158
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 0
+ },
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 61
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 24
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 300,
+ "survivors": 59,
+ "losses": 0,
+ "injured": 85,
+ "lightly_injured": 156,
+ "kills": 14
+ },
+ "lancer_t6": {
+ "troops": 600,
+ "survivors": 600,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 86
+ },
+ "marksman_t6": {
+ "troops": 200,
+ "survivors": 200,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 58
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 11,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_shield_saturation_20260926/renee_100l_vs_gatot_100i.json
===================================================================
--- testcases/emulator_verified/renee_shield_saturation_20260926/renee_100l_vs_gatot_100i.json prev run
+++ testcases/emulator_verified/renee_shield_saturation_20260926/renee_100l_vs_gatot_100i.json this run
@@ -0,0 +1,149 @@
+[
+ {
+ "test_id": "renee_100l_vs_gatot_100i",
+ "description": "C2 battle report 2734693171383452",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 607.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 283.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gatot": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t9": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 627.31,
+ "defense": 619.86,
+ "lethality": 230.66,
+ "health": 281.02
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T08:18:48.000Z",
+ "randomseed": 1790410729,
+ "attacker": 0,
+ "defender": 91,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171383452",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T08-19-05.115Z-a02a4078-c115-4800-9731-f6cfd892c3af.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 0
+ },
+ "defender": {
+ "troops": 100,
+ "survivors": 91,
+ "losses": 0,
+ "injured": 4,
+ "lightly_injured": 5,
+ "kills": 35
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 0
+ }
+ },
+ "defender": {
+ "infantry_t9": {
+ "troops": 100,
+ "survivors": 91,
+ "losses": 0,
+ "injured": 4,
+ "lightly_injured": 5,
+ "kills": 35
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 28,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": 56,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_shield_saturation_20260926/renee_100l_vs_gatot_50i.json
===================================================================
--- testcases/emulator_verified/renee_shield_saturation_20260926/renee_100l_vs_gatot_50i.json prev run
+++ testcases/emulator_verified/renee_shield_saturation_20260926/renee_100l_vs_gatot_50i.json this run
@@ -0,0 +1,149 @@
+[
+ {
+ "test_id": "renee_100l_vs_gatot_50i",
+ "description": "C2 battle report 2734693171383435",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 607.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 283.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gatot": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t9": 50
+ },
+ "stats": {
+ "infantry": {
+ "attack": 627.31,
+ "defense": 619.86,
+ "lethality": 230.66,
+ "health": 281.02
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T08:17:04.000Z",
+ "randomseed": 1790410625,
+ "attacker": 0,
+ "defender": 37,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171383435",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T08-17-21.127Z-bb32ec56-861a-4270-8a5d-b3dcc22ea773.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 0
+ },
+ "defender": {
+ "troops": 50,
+ "survivors": 37,
+ "losses": 0,
+ "injured": 5,
+ "lightly_injured": 8,
+ "kills": 35
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 0
+ }
+ },
+ "defender": {
+ "infantry_t9": {
+ "troops": 50,
+ "survivors": 37,
+ "losses": 0,
+ "injured": 5,
+ "lightly_injured": 8,
+ "kills": 35
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 59,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": 118,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_stun_20260925/renee_300l_vs_sonya_250l.json
===================================================================
--- testcases/emulator_verified/renee_stun_20260925/renee_300l_vs_sonya_250l.json prev run
+++ testcases/emulator_verified/renee_stun_20260925/renee_300l_vs_sonya_250l.json this run
@@ -0,0 +1,153 @@
+[
+ {
+ "test_id": "renee_300l_vs_sonya_250l",
+ "description": "C2 battle report 2734693171360925",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Sonya": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 250
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 534.96,
+ "defense": 534.21,
+ "lethality": 323.16,
+ "health": 285.53
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T17:08:58.000Z",
+ "randomseed": 1790356139,
+ "attacker": 61,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171360925",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T17-09-23.230Z-a12aa5a9-8dae-49eb-88c5-9cdce4817024.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 300,
+ "survivors": 61,
+ "losses": 0,
+ "injured": 84,
+ "lightly_injured": 155,
+ "kills": 88
+ },
+ "defender": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 84
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 61,
+ "losses": 0,
+ "injured": 84,
+ "lightly_injured": 155,
+ "kills": 88
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 84
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 15,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 15,
+ "displayed_kills": 4
+ },
+ "Torrential Impact": {
+ "triggered": 6,
+ "displayed_kills": 6
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_stun_20260925/renee_400l_vs_sonya_400l.json
===================================================================
--- testcases/emulator_verified/renee_stun_20260925/renee_400l_vs_sonya_400l.json prev run
+++ testcases/emulator_verified/renee_stun_20260925/renee_400l_vs_sonya_400l.json this run
@@ -0,0 +1,153 @@
+[
+ {
+ "test_id": "renee_400l_vs_sonya_400l",
+ "description": "C2 battle report 2734693171360981",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 400
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Sonya": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 400
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 534.96,
+ "defense": 534.21,
+ "lethality": 323.16,
+ "health": 285.53
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T17:11:58.000Z",
+ "randomseed": 1790356319,
+ "attacker": 0,
+ "defender": 106,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171360981",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T17-12-23.230Z-aac76978-f650-41e7-b7c7-fd782808d75f.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 103
+ },
+ "defender": {
+ "troops": 400,
+ "survivors": 106,
+ "losses": 0,
+ "injured": 103,
+ "lightly_injured": 191,
+ "kills": 140
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 103
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 400,
+ "survivors": 106,
+ "losses": 0,
+ "injured": 103,
+ "lightly_injured": 191,
+ "kills": 140
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 13,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 13,
+ "displayed_kills": 8
+ },
+ "Torrential Impact": {
+ "triggered": 5,
+ "displayed_kills": 10
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_turn_boundary_20260927/calibration_renee.json
===================================================================
--- testcases/emulator_verified/renee_turn_boundary_20260927/calibration_renee.json prev run
+++ testcases/emulator_verified/renee_turn_boundary_20260927/calibration_renee.json this run
@@ -0,0 +1,153 @@
+[
+ {
+ "test_id": "calibration_renee",
+ "description": "C2 battle report 2734693171417030",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 200,
+ "lancer_t6": 300,
+ "marksman_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 1500
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T23:40:10.000Z",
+ "randomseed": 1790466011,
+ "attacker": 733,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171417030",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T23-40-25.813Z-f374f77f-22b5-46b1-8f29-118cfd93ca6a.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 800,
+ "survivors": 733,
+ "losses": 0,
+ "injured": 24,
+ "lightly_injured": 43,
+ "kills": 525
+ },
+ "defender": {
+ "troops": 1500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 525,
+ "lightly_injured": 975,
+ "kills": 24
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 133,
+ "losses": 0,
+ "injured": 24,
+ "lightly_injured": 43,
+ "kills": 32
+ },
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 300,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 315
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 300,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 178
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 525,
+ "lightly_injured": 975,
+ "kills": 24
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 17,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_turn_boundary_20260927/calibration_renee_gwen.json
===================================================================
--- testcases/emulator_verified/renee_turn_boundary_20260927/calibration_renee_gwen.json prev run
+++ testcases/emulator_verified/renee_turn_boundary_20260927/calibration_renee_gwen.json this run
@@ -0,0 +1,169 @@
+[
+ {
+ "test_id": "calibration_renee_gwen",
+ "description": "C2 battle report 2734693171416967",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 200,
+ "lancer_t6": 300,
+ "marksman_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 1500
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T23:37:58.000Z",
+ "randomseed": 1790465879,
+ "attacker": 745,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171416967",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T23-38-13.909Z-e8c82022-fcf3-4823-b9d7-059a6c284b15.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 800,
+ "survivors": 745,
+ "losses": 0,
+ "injured": 20,
+ "lightly_injured": 35,
+ "kills": 525
+ },
+ "defender": {
+ "troops": 1500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 525,
+ "lightly_injured": 975,
+ "kills": 20
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 145,
+ "losses": 0,
+ "injured": 20,
+ "lightly_injured": 35,
+ "kills": 28
+ },
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 300,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 274
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 300,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 223
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 525,
+ "lightly_injured": 975,
+ "kills": 20
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 12,
+ "displayed_kills": 8
+ },
+ "Blastmaster": {
+ "triggered": 5,
+ "displayed_kills": 3
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 14,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_turn_boundary_20260927/combined_infantry_probe.json
===================================================================
--- testcases/emulator_verified/renee_turn_boundary_20260927/combined_infantry_probe.json prev run
+++ testcases/emulator_verified/renee_turn_boundary_20260927/combined_infantry_probe.json this run
@@ -0,0 +1,258 @@
+[
+ {
+ "test_id": "combined_infantry_probe",
+ "description": "C2 battle report 2734693171417101",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 50,
+ "lancer_t6": 50,
+ "marksman_t6": 200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 1446
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T23:47:07.000Z",
+ "randomseed": 1790466428,
+ "attacker": 8,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171417101",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T23-47-22.846Z-da3fd037-94b2-485d-90b3-e566a3d549f1.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 300,
+ "survivors": 8,
+ "losses": 0,
+ "injured": 104,
+ "lightly_injured": 188,
+ "kills": 507
+ },
+ "defender": {
+ "troops": 1446,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 507,
+ "lightly_injured": 939,
+ "kills": 104
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 6
+ },
+ "lancer_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 105
+ },
+ "marksman_t6": {
+ "troops": 200,
+ "survivors": 8,
+ "losses": 0,
+ "injured": 68,
+ "lightly_injured": 124,
+ "kills": 396
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1446,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 507,
+ "lightly_injured": 939,
+ "kills": 104
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 42,
+ "displayed_kills": 10
+ },
+ "Blastmaster": {
+ "triggered": 34,
+ "displayed_kills": 6
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 86,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-26T23:47:50.000Z",
+ "randomseed": 1790466471,
+ "attacker": 8,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171417107",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T23-48-05.900Z-1c0a6586-c5f6-494c-8778-48bd60da36bd.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 300,
+ "survivors": 8,
+ "losses": 0,
+ "injured": 104,
+ "lightly_injured": 188,
+ "kills": 507
+ },
+ "defender": {
+ "troops": 1446,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 507,
+ "lightly_injured": 939,
+ "kills": 104
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 6
+ },
+ "lancer_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 105
+ },
+ "marksman_t6": {
+ "troops": 200,
+ "survivors": 8,
+ "losses": 0,
+ "injured": 68,
+ "lightly_injured": 124,
+ "kills": 396
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1446,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 507,
+ "lightly_injured": 939,
+ "kills": 104
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 42,
+ "displayed_kills": 10
+ },
+ "Blastmaster": {
+ "triggered": 34,
+ "displayed_kills": 6
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 86,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_turn_boundary_20260927/combined_lancer_probe.json
===================================================================
--- testcases/emulator_verified/renee_turn_boundary_20260927/combined_lancer_probe.json prev run
+++ testcases/emulator_verified/renee_turn_boundary_20260927/combined_lancer_probe.json this run
@@ -0,0 +1,258 @@
+[
+ {
+ "test_id": "combined_lancer_probe",
+ "description": "C2 battle report 2734693171417130",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 50,
+ "lancer_t6": 200,
+ "marksman_t6": 50
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 439.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 2567
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T23:50:08.000Z",
+ "randomseed": 1790466609,
+ "attacker": 26,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171417130",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T23-50-23.840Z-b5a42875-03e4-4680-9527-2d6efa8d2458.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 300,
+ "survivors": 26,
+ "losses": 0,
+ "injured": 97,
+ "lightly_injured": 177,
+ "kills": 899
+ },
+ "defender": {
+ "troops": 2567,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 899,
+ "lightly_injured": 1668,
+ "kills": 97
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 7
+ },
+ "lancer_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 538
+ },
+ "marksman_t6": {
+ "troops": 50,
+ "survivors": 26,
+ "losses": 0,
+ "injured": 9,
+ "lightly_injured": 15,
+ "kills": 354
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 2567,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 899,
+ "lightly_injured": 1668,
+ "kills": 97
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 17,
+ "displayed_kills": 14
+ },
+ "Blastmaster": {
+ "triggered": 11,
+ "displayed_kills": 4
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 28,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-26T23:50:51.000Z",
+ "randomseed": 1790466652,
+ "attacker": 26,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171417133",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T23-51-06.889Z-7a07d930-ece4-4475-b142-d955f93e3b16.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 300,
+ "survivors": 26,
+ "losses": 0,
+ "injured": 97,
+ "lightly_injured": 177,
+ "kills": 899
+ },
+ "defender": {
+ "troops": 2567,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 899,
+ "lightly_injured": 1668,
+ "kills": 97
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 7
+ },
+ "lancer_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 538
+ },
+ "marksman_t6": {
+ "troops": 50,
+ "survivors": 26,
+ "losses": 0,
+ "injured": 9,
+ "lightly_injured": 15,
+ "kills": 354
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 2567,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 899,
+ "lightly_injured": 1668,
+ "kills": 97
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 17,
+ "displayed_kills": 14
+ },
+ "Blastmaster": {
+ "triggered": 11,
+ "displayed_kills": 4
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 28,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_turn_boundary_20260927/pure_renee_matched_control.json
===================================================================
--- testcases/emulator_verified/renee_turn_boundary_20260927/pure_renee_matched_control.json prev run
+++ testcases/emulator_verified/renee_turn_boundary_20260927/pure_renee_matched_control.json this run
@@ -0,0 +1,232 @@
+[
+ {
+ "test_id": "pure_renee_matched_control",
+ "description": "C2 battle report 2734693171417109",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 50,
+ "lancer_t6": 50,
+ "marksman_t6": 200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 1446
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-26T23:48:38.000Z",
+ "randomseed": 1790466519,
+ "attacker": 0,
+ "defender": 654,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171417109",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T23-48-53.831Z-18cabd46-c439-4e3b-8f82-c9292cc19b32.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 106,
+ "lightly_injured": 194,
+ "kills": 278
+ },
+ "defender": {
+ "troops": 1446,
+ "survivors": 654,
+ "losses": 0,
+ "injured": 278,
+ "lightly_injured": 514,
+ "kills": 106
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 4
+ },
+ "lancer_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 96
+ },
+ "marksman_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 178
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1446,
+ "survivors": 654,
+ "losses": 0,
+ "injured": 278,
+ "lightly_injured": 514,
+ "kills": 106
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 47,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-26T23:49:21.000Z",
+ "randomseed": 1790466562,
+ "attacker": 0,
+ "defender": 654,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171417127",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-26T23-49-36.907Z-c2a204e3-8e49-458c-89a0-aef6c1e8d615.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 106,
+ "lightly_injured": 194,
+ "kills": 278
+ },
+ "defender": {
+ "troops": 1446,
+ "survivors": 654,
+ "losses": 0,
+ "injured": 278,
+ "lightly_injured": 514,
+ "kills": 106
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 4
+ },
+ "lancer_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 96
+ },
+ "marksman_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 178
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1446,
+ "survivors": 654,
+ "losses": 0,
+ "injured": 278,
+ "lightly_injured": 514,
+ "kills": 106
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 47,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/scheduled_skill_stun_20260925/sonya_150l_vs_hendrik_250m.json
===================================================================
--- testcases/emulator_verified/scheduled_skill_stun_20260925/sonya_150l_vs_hendrik_250m.json prev run
+++ testcases/emulator_verified/scheduled_skill_stun_20260925/sonya_150l_vs_hendrik_250m.json this run
@@ -0,0 +1,157 @@
+[
+ {
+ "test_id": "sonya_150l_vs_hendrik_250m",
+ "description": "C2 battle report 2734693171359198",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Sonya": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 150
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 534.96,
+ "defense": 534.21,
+ "lethality": 323.16,
+ "health": 285.53
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Hendrik": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "marksman_t6": 250
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 479,
+ "defense": 467.8,
+ "lethality": 244.2,
+ "health": 221.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T16:24:38.000Z",
+ "randomseed": 1790353479,
+ "attacker": 12,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171359198",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T16-24-54.205Z-967ecf59-8ca0-4f97-b173-4a75335917d8.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 150,
+ "survivors": 12,
+ "losses": 0,
+ "injured": 49,
+ "lightly_injured": 89,
+ "kills": 88
+ },
+ "defender": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 49
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 12,
+ "losses": 0,
+ "injured": 49,
+ "lightly_injured": 89,
+ "kills": 88
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 49
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 18,
+ "displayed_kills": 5
+ },
+ "Torrential Impact": {
+ "triggered": 7,
+ "displayed_kills": 6
+ }
+ }
+ },
+ "defender": {
+ "Hendrik": {
+ "Dagon's Heir": {
+ "triggered": 9,
+ "displayed_kills": 4
+ },
+ "Armor of Barnacles": {
+ "triggered": 9,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/scheduled_skill_stun_20260925/sonya_600l_vs_hendrik_1200m.json
===================================================================
--- testcases/emulator_verified/scheduled_skill_stun_20260925/sonya_600l_vs_hendrik_1200m.json prev run
+++ testcases/emulator_verified/scheduled_skill_stun_20260925/sonya_600l_vs_hendrik_1200m.json this run
@@ -0,0 +1,157 @@
+[
+ {
+ "test_id": "sonya_600l_vs_hendrik_1200m",
+ "description": "C2 battle report 2734693171358881",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Sonya": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 600
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 534.96,
+ "defense": 534.21,
+ "lethality": 323.16,
+ "health": 285.53
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Hendrik": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "marksman_t6": 1200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 479,
+ "defense": 467.8,
+ "lethality": 244.2,
+ "health": 221.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T16:21:53.000Z",
+ "randomseed": 1790353314,
+ "attacker": 0,
+ "defender": 422,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171358881",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T16-22-09.173Z-3fc45c38-73c6-4820-8615-efe4f2a86303.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 273
+ },
+ "defender": {
+ "troops": 1200,
+ "survivors": 422,
+ "losses": 0,
+ "injured": 273,
+ "lightly_injured": 505,
+ "kills": 210
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 273
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 1200,
+ "survivors": 422,
+ "losses": 0,
+ "injured": 273,
+ "lightly_injured": 505,
+ "kills": 210
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 14,
+ "displayed_kills": 16
+ },
+ "Torrential Impact": {
+ "triggered": 5,
+ "displayed_kills": 19
+ }
+ }
+ },
+ "defender": {
+ "Hendrik": {
+ "Dagon's Heir": {
+ "triggered": 7,
+ "displayed_kills": 14
+ },
+ "Armor of Barnacles": {
+ "triggered": 7,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/scheduled_skill_stun_20260925/wayne_500i_vs_sonya_150l.json
===================================================================
--- testcases/emulator_verified/scheduled_skill_stun_20260925/wayne_500i_vs_sonya_150l.json prev run
+++ testcases/emulator_verified/scheduled_skill_stun_20260925/wayne_500i_vs_sonya_150l.json this run
@@ -0,0 +1,489 @@
+[
+ {
+ "test_id": "wayne_500i_vs_sonya_150l",
+ "description": "C2 battle report 2734693171359275",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Wayne": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 500
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 402.68,
+ "defense": 391.48,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Sonya": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 150
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 534.96,
+ "defense": 534.21,
+ "lethality": 323.16,
+ "health": 285.53
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T16:26:01.000Z",
+ "randomseed": 1790353562,
+ "attacker": 0,
+ "defender": 35,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171359275",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T16-26-26.210Z-6e6e18e3-e1e3-4372-a8a5-bad051a511d3.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 41
+ },
+ "defender": {
+ "troops": 150,
+ "survivors": 35,
+ "losses": 0,
+ "injured": 41,
+ "lightly_injured": 74,
+ "kills": 175
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 41
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 35,
+ "losses": 0,
+ "injured": 41,
+ "lightly_injured": 74,
+ "kills": 175
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Wayne": {
+ "Fleet": {
+ "triggered": 15,
+ "displayed_kills": 3
+ },
+ "Thunder Strike": {
+ "triggered": 29,
+ "displayed_kills": 4
+ },
+ "Roundabout Hit": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 72,
+ "displayed_kills": 9
+ },
+ "Torrential Impact": {
+ "triggered": 28,
+ "displayed_kills": 15
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-25T16:27:05.000Z",
+ "randomseed": 1790353626,
+ "attacker": 0,
+ "defender": 37,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171359302",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T16-27-30.186Z-a5ce9c2f-d09b-4bae-b1f8-9181e56bbffe.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 40
+ },
+ "defender": {
+ "troops": 150,
+ "survivors": 37,
+ "losses": 0,
+ "injured": 40,
+ "lightly_injured": 73,
+ "kills": 175
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 40
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 37,
+ "losses": 0,
+ "injured": 40,
+ "lightly_injured": 73,
+ "kills": 175
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Wayne": {
+ "Fleet": {
+ "triggered": 13,
+ "displayed_kills": 4
+ },
+ "Thunder Strike": {
+ "triggered": 28,
+ "displayed_kills": 3
+ },
+ "Roundabout Hit": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 71,
+ "displayed_kills": 12
+ },
+ "Torrential Impact": {
+ "triggered": 28,
+ "displayed_kills": 13
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-25T16:28:09.000Z",
+ "randomseed": 1790353690,
+ "attacker": 0,
+ "defender": 26,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171359420",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T16-28-34.173Z-ea7cc5d2-56a8-4599-9b78-dbc4bbe5451c.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 44
+ },
+ "defender": {
+ "troops": 150,
+ "survivors": 26,
+ "losses": 0,
+ "injured": 44,
+ "lightly_injured": 80,
+ "kills": 175
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 44
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 26,
+ "losses": 0,
+ "injured": 44,
+ "lightly_injured": 80,
+ "kills": 175
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Wayne": {
+ "Fleet": {
+ "triggered": 23,
+ "displayed_kills": 5
+ },
+ "Thunder Strike": {
+ "triggered": 30,
+ "displayed_kills": 4
+ },
+ "Roundabout Hit": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 75,
+ "displayed_kills": 10
+ },
+ "Torrential Impact": {
+ "triggered": 30,
+ "displayed_kills": 14
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-25T16:29:13.000Z",
+ "randomseed": 1790353754,
+ "attacker": 0,
+ "defender": 30,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171359563",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T16-29-38.163Z-ef2133e3-2d47-4d92-8834-66962db4f042.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 42
+ },
+ "defender": {
+ "troops": 150,
+ "survivors": 30,
+ "losses": 0,
+ "injured": 42,
+ "lightly_injured": 78,
+ "kills": 175
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 42
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 30,
+ "losses": 0,
+ "injured": 42,
+ "lightly_injured": 78,
+ "kills": 175
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Wayne": {
+ "Fleet": {
+ "triggered": 19,
+ "displayed_kills": 4
+ },
+ "Thunder Strike": {
+ "triggered": 29,
+ "displayed_kills": 4
+ },
+ "Roundabout Hit": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 73,
+ "displayed_kills": 13
+ },
+ "Torrential Impact": {
+ "triggered": 29,
+ "displayed_kills": 14
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-25T16:30:17.000Z",
+ "randomseed": 1790353818,
+ "attacker": 0,
+ "defender": 43,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171359749",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T16-30-42.172Z-b7aa6a22-1d94-4009-b48b-fc9f0a6ee572.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 38
+ },
+ "defender": {
+ "troops": 150,
+ "survivors": 43,
+ "losses": 0,
+ "injured": 38,
+ "lightly_injured": 69,
+ "kills": 175
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 38
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 43,
+ "losses": 0,
+ "injured": 38,
+ "lightly_injured": 69,
+ "kills": 175
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Wayne": {
+ "Fleet": {
+ "triggered": 11,
+ "displayed_kills": 2
+ },
+ "Thunder Strike": {
+ "triggered": 27,
+ "displayed_kills": 4
+ },
+ "Roundabout Hit": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 68,
+ "displayed_kills": 12
+ },
+ "Torrential Impact": {
+ "triggered": 27,
+ "displayed_kills": 14
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/skill_buckets_20260928/control_edith.json
===================================================================
--- testcases/emulator_verified/skill_buckets_20260928/control_edith.json prev run
+++ testcases/emulator_verified/skill_buckets_20260928/control_edith.json this run
@@ -0,0 +1,127 @@
+[
+ {
+ "test_id": "control_edith",
+ "description": "C2 battle report 2734693171494518",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "marksman_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Edith": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "marksman_t6": 1600
+ },
+ "stats": {
+ "infantry": {
+ "attack": 401.82,
+ "defense": 394.37,
+ "lethality": 193.33,
+ "health": 198.83
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-28T11:57:05.000Z",
+ "randomseed": 1790596626,
+ "attacker": 0,
+ "defender": 253,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171494518",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-28T11-57-22.133Z-d2210b4b-5d72-4e97-85f4-36f980d409b2.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 472
+ },
+ "defender": {
+ "troops": 1600,
+ "survivors": 253,
+ "losses": 0,
+ "injured": 472,
+ "lightly_injured": 875,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "marksman_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 472
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 1600,
+ "survivors": 253,
+ "losses": 0,
+ "injured": 472,
+ "lightly_injured": 875,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/skill_buckets_20260928/control_gatot.json
===================================================================
--- testcases/emulator_verified/skill_buckets_20260928/control_gatot.json prev run
+++ testcases/emulator_verified/skill_buckets_20260928/control_gatot.json this run
@@ -0,0 +1,135 @@
+[
+ {
+ "test_id": "control_gatot",
+ "description": "C2 battle report 2734693171494510",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "marksman_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gatot": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "marksman_t6": 1600
+ },
+ "stats": {
+ "infantry": {
+ "attack": 627.31,
+ "defense": 619.86,
+ "lethality": 230.66,
+ "health": 281.02
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-28T11:57:01.000Z",
+ "randomseed": 1790596622,
+ "attacker": 0,
+ "defender": 455,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171494510",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-28T11-57-17.129Z-d4028e1e-1730-4e52-b354-8a6350942045.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 401
+ },
+ "defender": {
+ "troops": 1600,
+ "survivors": 455,
+ "losses": 0,
+ "injured": 401,
+ "lightly_injured": 744,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "marksman_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 401
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 1600,
+ "survivors": 455,
+ "losses": 0,
+ "injured": 401,
+ "lightly_injured": 744,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/skill_buckets_20260928/control_jessie.json
===================================================================
--- testcases/emulator_verified/skill_buckets_20260928/control_jessie.json prev run
+++ testcases/emulator_verified/skill_buckets_20260928/control_jessie.json this run
@@ -0,0 +1,126 @@
+[
+ {
+ "test_id": "control_jessie",
+ "description": "C2 battle report 2734693171494632",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "marksman_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Jessie": {
+ "skill_1": 1,
+ "skill_2": 1
+ }
+ },
+ "troops": {
+ "marksman_t6": 1600
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 297.72,
+ "defense": 297.02,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-28T11:58:35.000Z",
+ "randomseed": 1790596716,
+ "attacker": 0,
+ "defender": 276,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171494632",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-28T11-58-52.138Z-835f14e4-8e33-4f5f-a91d-6e9d35d75b73.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 464
+ },
+ "defender": {
+ "troops": 1600,
+ "survivors": 276,
+ "losses": 0,
+ "injured": 464,
+ "lightly_injured": 860,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "marksman_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 464
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 1600,
+ "survivors": 276,
+ "losses": 0,
+ "injured": 464,
+ "lightly_injured": 860,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/skill_buckets_20260928/control_ling.json
===================================================================
--- testcases/emulator_verified/skill_buckets_20260928/control_ling.json prev run
+++ testcases/emulator_verified/skill_buckets_20260928/control_ling.json this run
@@ -0,0 +1,126 @@
+[
+ {
+ "test_id": "control_ling",
+ "description": "C2 battle report 2734693171494630",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "marksman_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Ling Xue": {
+ "skill_1": 1,
+ "skill_2": 1
+ }
+ },
+ "troops": {
+ "marksman_t6": 1600
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 256.45,
+ "defense": 255.75,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-28T11:58:34.000Z",
+ "randomseed": 1790596715,
+ "attacker": 0,
+ "defender": 155,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171494630",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-28T11-58-50.135Z-e3817263-c1e0-4a04-8aec-203b1d5c8db6.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 506
+ },
+ "defender": {
+ "troops": 1600,
+ "survivors": 155,
+ "losses": 0,
+ "injured": 506,
+ "lightly_injured": 939,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "marksman_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 506
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 1600,
+ "survivors": 155,
+ "losses": 0,
+ "injured": 506,
+ "lightly_injured": 939,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/skill_buckets_20260928/control_lumak.json
===================================================================
--- testcases/emulator_verified/skill_buckets_20260928/control_lumak.json prev run
+++ testcases/emulator_verified/skill_buckets_20260928/control_lumak.json this run
@@ -0,0 +1,126 @@
+[
+ {
+ "test_id": "control_lumak",
+ "description": "C2 battle report 2734693171494627",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "marksman_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Lumak Bokan": {
+ "skill_1": 3,
+ "skill_2": 1
+ }
+ },
+ "troops": {
+ "marksman_t6": 1600
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 266.12,
+ "defense": 265.42,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-28T11:58:31.000Z",
+ "randomseed": 1790596712,
+ "attacker": 0,
+ "defender": 333,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171494627",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-28T11-58-47.130Z-8a2afa27-72cb-476f-80c6-f9e7b9e912cf.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 444
+ },
+ "defender": {
+ "troops": 1600,
+ "survivors": 333,
+ "losses": 0,
+ "injured": 444,
+ "lightly_injured": 823,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "marksman_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 444
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 1600,
+ "survivors": 333,
+ "losses": 0,
+ "injured": 444,
+ "lightly_injured": 823,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/skill_buckets_20260928/control_none.json
===================================================================
--- testcases/emulator_verified/skill_buckets_20260928/control_none.json prev run
+++ testcases/emulator_verified/skill_buckets_20260928/control_none.json this run
@@ -0,0 +1,121 @@
+[
+ {
+ "test_id": "control_none",
+ "description": "C2 battle report 2734693171494513",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "marksman_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "marksman_t6": 1600
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-28T11:57:03.000Z",
+ "randomseed": 1790596624,
+ "attacker": 19,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171494513",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-28T11-57-19.129Z-82839fe4-687d-4555-b57a-5bf731d298f9.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 19,
+ "losses": 0,
+ "injured": 344,
+ "lightly_injured": 637,
+ "kills": 560
+ },
+ "defender": {
+ "troops": 1600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 560,
+ "lightly_injured": 1040,
+ "kills": 344
+ },
+ "troop_details": {
+ "attacker": {
+ "marksman_t6": {
+ "troops": 1000,
+ "survivors": 19,
+ "losses": 0,
+ "injured": 344,
+ "lightly_injured": 637,
+ "kills": 560
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 1600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 560,
+ "lightly_injured": 1040,
+ "kills": 344
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/skill_buckets_20260928/edith_jessie.json
===================================================================
--- testcases/emulator_verified/skill_buckets_20260928/edith_jessie.json prev run
+++ testcases/emulator_verified/skill_buckets_20260928/edith_jessie.json this run
@@ -0,0 +1,131 @@
+[
+ {
+ "test_id": "edith_jessie",
+ "description": "C2 battle report 2734693171494661",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "marksman_t6": 1110
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Edith": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ },
+ "Jessie": {
+ "skill_1": 1,
+ "skill_2": 1
+ }
+ },
+ "troops": {
+ "marksman_t6": 1600
+ },
+ "stats": {
+ "infantry": {
+ "attack": 401.82,
+ "defense": 394.37,
+ "lethality": 193.33,
+ "health": 198.83
+ },
+ "lancer": {
+ "attack": 297.72,
+ "defense": 297.02,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-28T12:01:05.000Z",
+ "randomseed": 1790596866,
+ "attacker": 0,
+ "defender": 17,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171494661",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-28T12-01-21.135Z-932aac26-79c8-4ab5-86ad-038e6ca39e00.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1110,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 389,
+ "lightly_injured": 721,
+ "kills": 555
+ },
+ "defender": {
+ "troops": 1600,
+ "survivors": 17,
+ "losses": 0,
+ "injured": 555,
+ "lightly_injured": 1028,
+ "kills": 389
+ },
+ "troop_details": {
+ "attacker": {
+ "marksman_t6": {
+ "troops": 1110,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 389,
+ "lightly_injured": 721,
+ "kills": 555
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 1600,
+ "survivors": 17,
+ "losses": 0,
+ "injured": 555,
+ "lightly_injured": 1028,
+ "kills": 389
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/skill_buckets_20260928/edith_ling.json
===================================================================
--- testcases/emulator_verified/skill_buckets_20260928/edith_ling.json prev run
+++ testcases/emulator_verified/skill_buckets_20260928/edith_ling.json this run
@@ -0,0 +1,131 @@
+[
+ {
+ "test_id": "edith_ling",
+ "description": "C2 battle report 2734693171494667",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "marksman_t6": 1070
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Edith": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ },
+ "Ling Xue": {
+ "skill_1": 1,
+ "skill_2": 1
+ }
+ },
+ "troops": {
+ "marksman_t6": 1600
+ },
+ "stats": {
+ "infantry": {
+ "attack": 401.82,
+ "defense": 394.37,
+ "lethality": 193.33,
+ "health": 198.83
+ },
+ "lancer": {
+ "attack": 256.45,
+ "defense": 255.75,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-28T12:02:03.000Z",
+ "randomseed": 1790596924,
+ "attacker": 0,
+ "defender": 66,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171494667",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-28T12-02-19.132Z-c704efe9-5f35-4cc7-a1a9-9852a25c61af.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1070,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 375,
+ "lightly_injured": 695,
+ "kills": 537
+ },
+ "defender": {
+ "troops": 1600,
+ "survivors": 66,
+ "losses": 0,
+ "injured": 537,
+ "lightly_injured": 997,
+ "kills": 375
+ },
+ "troop_details": {
+ "attacker": {
+ "marksman_t6": {
+ "troops": 1070,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 375,
+ "lightly_injured": 695,
+ "kills": 537
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 1600,
+ "survivors": 66,
+ "losses": 0,
+ "injured": 537,
+ "lightly_injured": 997,
+ "kills": 375
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/skill_buckets_20260928/edith_lumak.json
===================================================================
--- testcases/emulator_verified/skill_buckets_20260928/edith_lumak.json prev run
+++ testcases/emulator_verified/skill_buckets_20260928/edith_lumak.json this run
@@ -0,0 +1,131 @@
+[
+ {
+ "test_id": "edith_lumak",
+ "description": "C2 battle report 2734693171494648",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "marksman_t6": 1130
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Edith": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ },
+ "Lumak Bokan": {
+ "skill_1": 3,
+ "skill_2": 1
+ }
+ },
+ "troops": {
+ "marksman_t6": 1600
+ },
+ "stats": {
+ "infantry": {
+ "attack": 401.82,
+ "defense": 394.37,
+ "lethality": 193.33,
+ "health": 198.83
+ },
+ "lancer": {
+ "attack": 266.12,
+ "defense": 265.42,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-28T11:59:39.000Z",
+ "randomseed": 1790596780,
+ "attacker": 0,
+ "defender": 25,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171494648",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-28T11-59-55.143Z-9d0d266c-1d92-4ffc-88c9-e2bafbe06fee.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1130,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 396,
+ "lightly_injured": 734,
+ "kills": 552
+ },
+ "defender": {
+ "troops": 1600,
+ "survivors": 25,
+ "losses": 0,
+ "injured": 552,
+ "lightly_injured": 1023,
+ "kills": 396
+ },
+ "troop_details": {
+ "attacker": {
+ "marksman_t6": {
+ "troops": 1130,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 396,
+ "lightly_injured": 734,
+ "kills": 552
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 1600,
+ "survivors": 25,
+ "losses": 0,
+ "injured": 552,
+ "lightly_injured": 1023,
+ "kills": 396
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/skill_buckets_20260928/gatot_jessie.json
===================================================================
--- testcases/emulator_verified/skill_buckets_20260928/gatot_jessie.json prev run
+++ testcases/emulator_verified/skill_buckets_20260928/gatot_jessie.json this run
@@ -0,0 +1,139 @@
+[
+ {
+ "test_id": "gatot_jessie",
+ "description": "C2 battle report 2734693171494670",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "marksman_t6": 1190
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gatot": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Jessie": {
+ "skill_1": 1,
+ "skill_2": 1
+ }
+ },
+ "troops": {
+ "marksman_t6": 1600
+ },
+ "stats": {
+ "infantry": {
+ "attack": 627.31,
+ "defense": 619.86,
+ "lethality": 230.66,
+ "health": 281.02
+ },
+ "lancer": {
+ "attack": 297.72,
+ "defense": 297.02,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-28T12:02:06.000Z",
+ "randomseed": 1790596927,
+ "attacker": 0,
+ "defender": 42,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171494670",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-28T12-02-22.121Z-96715f93-1687-4860-948e-a5f49a260e2c.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1190,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 417,
+ "lightly_injured": 773,
+ "kills": 546
+ },
+ "defender": {
+ "troops": 1600,
+ "survivors": 42,
+ "losses": 0,
+ "injured": 546,
+ "lightly_injured": 1012,
+ "kills": 417
+ },
+ "troop_details": {
+ "attacker": {
+ "marksman_t6": {
+ "troops": 1190,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 417,
+ "lightly_injured": 773,
+ "kills": 546
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 1600,
+ "survivors": 42,
+ "losses": 0,
+ "injured": 546,
+ "lightly_injured": 1012,
+ "kills": 417
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/skill_buckets_20260928/gatot_ling.json
===================================================================
--- testcases/emulator_verified/skill_buckets_20260928/gatot_ling.json prev run
+++ testcases/emulator_verified/skill_buckets_20260928/gatot_ling.json this run
@@ -0,0 +1,139 @@
+[
+ {
+ "test_id": "gatot_ling",
+ "description": "C2 battle report 2734693171494651",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "marksman_t6": 1150
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gatot": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Ling Xue": {
+ "skill_1": 1,
+ "skill_2": 1
+ }
+ },
+ "troops": {
+ "marksman_t6": 1600
+ },
+ "stats": {
+ "infantry": {
+ "attack": 627.31,
+ "defense": 619.86,
+ "lethality": 230.66,
+ "health": 281.02
+ },
+ "lancer": {
+ "attack": 256.45,
+ "defense": 255.75,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-28T11:59:42.000Z",
+ "randomseed": 1790596783,
+ "attacker": 0,
+ "defender": 16,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171494651",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-28T11-59-58.126Z-b3ed1a33-d75c-4323-a002-563e671499a1.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 403,
+ "lightly_injured": 747,
+ "kills": 555
+ },
+ "defender": {
+ "troops": 1600,
+ "survivors": 16,
+ "losses": 0,
+ "injured": 555,
+ "lightly_injured": 1029,
+ "kills": 403
+ },
+ "troop_details": {
+ "attacker": {
+ "marksman_t6": {
+ "troops": 1150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 403,
+ "lightly_injured": 747,
+ "kills": 555
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 1600,
+ "survivors": 16,
+ "losses": 0,
+ "injured": 555,
+ "lightly_injured": 1029,
+ "kills": 403
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/skill_buckets_20260928/gatot_lumak.json
===================================================================
--- testcases/emulator_verified/skill_buckets_20260928/gatot_lumak.json prev run
+++ testcases/emulator_verified/skill_buckets_20260928/gatot_lumak.json this run
@@ -0,0 +1,139 @@
+[
+ {
+ "test_id": "gatot_lumak",
+ "description": "C2 battle report 2734693171494663",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "marksman_t6": 1210
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gatot": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Lumak Bokan": {
+ "skill_1": 3,
+ "skill_2": 1
+ }
+ },
+ "troops": {
+ "marksman_t6": 1600
+ },
+ "stats": {
+ "infantry": {
+ "attack": 627.31,
+ "defense": 619.86,
+ "lethality": 230.66,
+ "health": 281.02
+ },
+ "lancer": {
+ "attack": 266.12,
+ "defense": 265.42,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-28T12:01:08.000Z",
+ "randomseed": 1790596869,
+ "attacker": 0,
+ "defender": 44,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171494663",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-28T12-01-24.139Z-8d199e5a-6c8d-4a33-9700-38923a5fb591.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1210,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 424,
+ "lightly_injured": 786,
+ "kills": 545
+ },
+ "defender": {
+ "troops": 1600,
+ "survivors": 44,
+ "losses": 0,
+ "injured": 545,
+ "lightly_injured": 1011,
+ "kills": 424
+ },
+ "troop_details": {
+ "attacker": {
+ "marksman_t6": {
+ "troops": 1210,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 424,
+ "lightly_injured": 786,
+ "kills": 545
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 1600,
+ "survivors": 44,
+ "losses": 0,
+ "injured": 545,
+ "lightly_injured": 1011,
+ "kills": 424
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/skill_buckets_20260928/sergey_ling_level3_matched.json
===================================================================
--- testcases/emulator_verified/skill_buckets_20260928/sergey_ling_level3_matched.json prev run
+++ testcases/emulator_verified/skill_buckets_20260928/sergey_ling_level3_matched.json this run
@@ -0,0 +1,130 @@
+[
+ {
+ "test_id": "sergey_ling_level3_matched",
+ "description": "C2 battle report 2734693171496487",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "marksman_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Sergey": {
+ "skill_1": 2,
+ "skill_2": 2
+ },
+ "Ling Xue": {
+ "skill_1": 3,
+ "skill_2": 1
+ }
+ },
+ "troops": {
+ "marksman_t6": 1600
+ },
+ "stats": {
+ "infantry": {
+ "attack": 346.14,
+ "defense": 338.69,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 256.45,
+ "defense": 255.75,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-28T13:03:31.000Z",
+ "randomseed": 1790600612,
+ "attacker": 0,
+ "defender": 569,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171496487",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-28T13-03-47.127Z-35ac5e5b-6386-48ee-a48f-9e464512d33b.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 361
+ },
+ "defender": {
+ "troops": 1600,
+ "survivors": 569,
+ "losses": 0,
+ "injured": 361,
+ "lightly_injured": 670,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "marksman_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 361
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 1600,
+ "survivors": 569,
+ "losses": 0,
+ "injured": 361,
+ "lightly_injured": 670,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/skill_buckets_20260928/sergey_ling_matched.json
===================================================================
--- testcases/emulator_verified/skill_buckets_20260928/sergey_ling_matched.json prev run
+++ testcases/emulator_verified/skill_buckets_20260928/sergey_ling_matched.json this run
@@ -0,0 +1,130 @@
+[
+ {
+ "test_id": "sergey_ling_matched",
+ "description": "C2 battle report 2734693171496167",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "marksman_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Sergey": {
+ "skill_1": 2,
+ "skill_2": 2
+ },
+ "Ling Xue": {
+ "skill_1": 1,
+ "skill_2": 1
+ }
+ },
+ "troops": {
+ "marksman_t6": 1600
+ },
+ "stats": {
+ "infantry": {
+ "attack": 346.14,
+ "defense": 338.69,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 256.45,
+ "defense": 255.75,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-28T12:52:00.000Z",
+ "randomseed": 1790599921,
+ "attacker": 0,
+ "defender": 468,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171496167",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-28T12-52-16.135Z-c9353ef0-220e-4fa5-943e-cfe104615f98.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 397
+ },
+ "defender": {
+ "troops": 1600,
+ "survivors": 468,
+ "losses": 0,
+ "injured": 397,
+ "lightly_injured": 735,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "marksman_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 397
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 1600,
+ "survivors": 468,
+ "losses": 0,
+ "injured": 397,
+ "lightly_injured": 735,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/skill_buckets_20260928/sergey_lumak_matched.json
===================================================================
--- testcases/emulator_verified/skill_buckets_20260928/sergey_lumak_matched.json prev run
+++ testcases/emulator_verified/skill_buckets_20260928/sergey_lumak_matched.json this run
@@ -0,0 +1,130 @@
+[
+ {
+ "test_id": "sergey_lumak_matched",
+ "description": "C2 battle report 2734693171496195",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "marksman_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Sergey": {
+ "skill_1": 2,
+ "skill_2": 2
+ },
+ "Lumak Bokan": {
+ "skill_1": 3,
+ "skill_2": 1
+ }
+ },
+ "troops": {
+ "marksman_t6": 1600
+ },
+ "stats": {
+ "infantry": {
+ "attack": 346.14,
+ "defense": 338.69,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 266.12,
+ "defense": 265.42,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-28T12:53:06.000Z",
+ "randomseed": 1790599987,
+ "attacker": 0,
+ "defender": 580,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171496195",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-28T12-53-22.137Z-19e6e485-8f82-4d93-af60-20bed14661ee.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 357
+ },
+ "defender": {
+ "troops": 1600,
+ "survivors": 580,
+ "losses": 0,
+ "injured": 357,
+ "lightly_injured": 663,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "marksman_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 357
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 1600,
+ "survivors": 580,
+ "losses": 0,
+ "injured": 357,
+ "lightly_injured": 663,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/wip_renee_gwen_no_blastmaster_20260925/wip_gwen_only_1000l_300m_vs_3000l.json
===================================================================
--- testcases/emulator_verified/wip_renee_gwen_no_blastmaster_20260925/wip_gwen_only_1000l_300m_vs_3000l.json prev run
+++ testcases/emulator_verified/wip_renee_gwen_no_blastmaster_20260925/wip_gwen_only_1000l_300m_vs_3000l.json this run
@@ -0,0 +1,143 @@
+[
+ {
+ "test_id": "wip_gwen_only_1000l_300m_vs_3000l",
+ "description": "C2 battle report 2734693171348034",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 1000,
+ "marksman_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 208.71,
+ "defense": 208.01,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 344.4,
+ "defense": 333.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 3000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T11:32:06.000Z",
+ "randomseed": 1790335927,
+ "attacker": 0,
+ "defender": 2320,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171348034",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T11-32-31.084Z-3864b2e5-edf5-4ad2-8936-2cb556d0c85f.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 455,
+ "lightly_injured": 845,
+ "kills": 238
+ },
+ "defender": {
+ "troops": 3000,
+ "survivors": 2320,
+ "losses": 0,
+ "injured": 238,
+ "lightly_injured": 442,
+ "kills": 455
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 83
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 155
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 3000,
+ "survivors": 2320,
+ "losses": 0,
+ "injured": 238,
+ "lightly_injured": 442,
+ "kills": 455
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 4,
+ "displayed_kills": 5
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/wip_renee_gwen_no_blastmaster_20260925/wip_renee_gwen_1000l_300m_vs_3000l.json
===================================================================
--- testcases/emulator_verified/wip_renee_gwen_no_blastmaster_20260925/wip_renee_gwen_1000l_300m_vs_3000l.json prev run
+++ testcases/emulator_verified/wip_renee_gwen_no_blastmaster_20260925/wip_renee_gwen_1000l_300m_vs_3000l.json this run
@@ -0,0 +1,155 @@
+[
+ {
+ "test_id": "wip_renee_gwen_1000l_300m_vs_3000l",
+ "description": "C2 battle report 2734693171347991",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 1000,
+ "marksman_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 344.4,
+ "defense": 333.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 3000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T11:29:09.000Z",
+ "randomseed": 1790335750,
+ "attacker": 747,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171347991",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T11-29-34.084Z-70edc78c-b413-4871-af89-8dec7060a27a.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1300,
+ "survivors": 747,
+ "losses": 0,
+ "injured": 194,
+ "lightly_injured": 359,
+ "kills": 1050
+ },
+ "defender": {
+ "troops": 3000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 1050,
+ "lightly_injured": 1950,
+ "kills": 194
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 447,
+ "losses": 0,
+ "injured": 194,
+ "lightly_injured": 359,
+ "kills": 741
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 300,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 309
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 3000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 1050,
+ "lightly_injured": 1950,
+ "kills": 194
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 6,
+ "displayed_kills": 16
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 11,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/wip_renee_gwen_no_blastmaster_20260925/wip_renee_gwen_1000l_300m_vs_4500l.json
===================================================================
--- testcases/emulator_verified/wip_renee_gwen_no_blastmaster_20260925/wip_renee_gwen_1000l_300m_vs_4500l.json prev run
+++ testcases/emulator_verified/wip_renee_gwen_no_blastmaster_20260925/wip_renee_gwen_1000l_300m_vs_4500l.json this run
@@ -0,0 +1,155 @@
+[
+ {
+ "test_id": "wip_renee_gwen_1000l_300m_vs_4500l",
+ "description": "C2 battle report 2734693171348058",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 1000,
+ "marksman_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 344.4,
+ "defense": 333.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 4500
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T11:33:30.000Z",
+ "randomseed": 1790336011,
+ "attacker": 0,
+ "defender": 1046,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171348058",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T11-33-55.086Z-ce82bc14-acd7-4786-819b-8905a462eb1e.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 455,
+ "lightly_injured": 845,
+ "kills": 1209
+ },
+ "defender": {
+ "troops": 4500,
+ "survivors": 1046,
+ "losses": 0,
+ "injured": 1209,
+ "lightly_injured": 2245,
+ "kills": 455
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 741
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 468
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 4500,
+ "survivors": 1046,
+ "losses": 0,
+ "injured": 1209,
+ "lightly_injured": 2245,
+ "kills": 455
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 9,
+ "displayed_kills": 21
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 17,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/wip_renee_gwen_no_blastmaster_20260925/wip_renee_only_1000l_300m_vs_3000l.json
===================================================================
--- testcases/emulator_verified/wip_renee_gwen_no_blastmaster_20260925/wip_renee_only_1000l_300m_vs_3000l.json prev run
+++ testcases/emulator_verified/wip_renee_gwen_no_blastmaster_20260925/wip_renee_only_1000l_300m_vs_3000l.json this run
@@ -0,0 +1,144 @@
+[
+ {
+ "test_id": "wip_renee_only_1000l_300m_vs_3000l",
+ "description": "C2 battle report 2734693171348029",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 1000,
+ "marksman_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 3000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 287.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 286.92,
+ "defense": 281.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 288.42,
+ "defense": 284.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T11:30:39.000Z",
+ "randomseed": 1790335840,
+ "attacker": 665,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171348029",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T11-31-04.084Z-e89f84db-5bf3-465e-8b57-df669a878ac2.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1300,
+ "survivors": 665,
+ "losses": 0,
+ "injured": 223,
+ "lightly_injured": 412,
+ "kills": 1050
+ },
+ "defender": {
+ "troops": 3000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 1050,
+ "lightly_injured": 1950,
+ "kills": 223
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 365,
+ "losses": 0,
+ "injured": 223,
+ "lightly_injured": 412,
+ "kills": 802
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 300,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 248
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 3000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 1050,
+ "lightly_injured": 1950,
+ "kills": 223
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 13,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/heroes_unittests/Flint_tc.json
===================================================================
--- testcases/heroes_unittests/Flint_tc.json prev run
+++ testcases/heroes_unittests/Flint_tc.json this run
@@ -1,216 +0,0 @@
-[
- {
- "test_id": "flint_tc_1",
- "time": "2025-07-20 - 03:49",
- "attacker": {
- "name": "daut",
- "heroes": {},
- "troops": {
- "infantry_t6": 1000,
- "lancer_t6": 500,
- "marksman_t6": 500
- },
- "stats": {
- "inf": {
- "attack": 9.94,
- "defense": 9.44,
- "lethality": 1.75,
- "health": 1.75
- },
- "lanc": {
- "attack": 9.94,
- "defense": 9.44,
- "lethality": 1.75,
- "health": 1.75
- },
- "mark": {
- "attack": 10.43,
- "defense": 9.44,
- "lethality": 1.75,
- "health": 1.75
- }
- },
- "joiner_heroes": {}
- },
- "defender": {
- "name": "viper",
- "heroes": {
- "Flint": {
- "skill_1": 4,
- "skill_2": 4,
- "skill_3": 4
- }
- },
- "troops": {
- "infantry_t6": 100,
- "lancer_t6": 50,
- "marksman_t6": 50
- },
- "stats": {
- "inf": {
- "attack": 313.87,
- "defense": 317.37,
- "lethality": 107.24,
- "health": 114.18
- },
- "lanc": {
- "attack": 167.0,
- "defense": 163.0,
- "lethality": 110.31,
- "health": 100.21
- },
- "mark": {
- "attack": 174.75,
- "defense": 168.25,
- "lethality": 117.75,
- "health": 104.74
- }
- },
- "joiner_heroes": {}
- },
- "sim_skills_used": {
- "attacker": {
- "MARKSMEN- Ranged Strike/1": "1 (22)"
- },
- "defender": {
- "Flint- Pyromaniac/1": "10 (24)",
- "Flint- Burning resolve/1": "1 (66)",
- "Flint- Immolation/1": "34 (102)",
- "INFANTRY- Master Brawler/1": "1 (2)",
- "LANCERS- Charge/1": "1 (2)",
- "MARKSMEN- Ranged Strike/1": "1 (18)"
- }
- },
- "game_report_result": [
- {
- "attacker": 0,
- "defender": 150
- },
- {
- "attacker": 0,
- "defender": 151
- },
- {
- "attacker": 0,
- "defender": 149
- },
- {
- "attacker": 0,
- "defender": 149
- },
- {
- "attacker": 0,
- "defender": 150
- }
- ]
- },
- {
- "test_id": "flint_tc_2",
- "time": "2025-07-20 - 04:07",
- "attacker": {
- "name": "daut",
- "heroes": {},
- "troops": {
- "infantry_t6": 3000,
- "lancer_t6": 2000,
- "marksman_t6": 2000
- },
- "stats": {
- "inf": {
- "attack": 9.94,
- "defense": 9.44,
- "lethality": 1.75,
- "health": 1.75
- },
- "lanc": {
- "attack": 9.94,
- "defense": 9.44,
- "lethality": 1.75,
- "health": 1.75
- },
- "mark": {
- "attack": 10.43,
- "defense": 9.44,
- "lethality": 1.75,
- "health": 1.75
- }
- },
- "joiner_heroes": {}
- },
- "defender": {
- "name": "viper",
- "heroes": {
- "Flint": {
- "skill_1": 4,
- "skill_2": 4,
- "skill_3": 4
- }
- },
- "troops": {
- "infantry_t6": 100,
- "lancer_t6": 50,
- "marksman_t6": 50
- },
- "stats": {
- "inf": {
- "attack": 313.87,
- "defense": 317.37,
- "lethality": 107.24,
- "health": 114.18
- },
- "lanc": {
- "attack": 167.0,
- "defense": 163.0,
- "lethality": 110.31,
- "health": 100.21
- },
- "mark": {
- "attack": 174.75,
- "defense": 168.25,
- "lethality": 117.75,
- "health": 104.74
- }
- },
- "joiner_heroes": {}
- },
- "sim_skills_used": {
- "attacker": {
- "INFANTRY- Master Brawler/1": "1 (1)",
- "LANCERS- Charge/1": "1 (5)",
- "MARKSMEN- Ranged Strike/1": "1 (46)"
- },
- "defender": {
- "Flint- Pyromaniac/1": "34 (100)",
- "Flint- Burning resolve/1": "1 (155)",
- "Flint- Immolation/1": "85 (245)",
- "MARKSMEN- Ranged Strike/1": "1 (47)"
- }
- },
- "game_report_result": [
- {
- "attacker": 5720,
- "defender": 0
- },
- {
- "attacker": 5756,
- "defender": 0
- },
- {
- "attacker": 5736,
- "defender": 0
- },
- {
- "attacker": 5715,
- "defender": 0
- },
- {
- "attacker": 5733,
- "defender": 0
- },
- {
- "attacker": 5736,
- "defender": 0
- }
- ]
- }
-]
\ No newline at end of file
Index: testcases/heroes_unittests/Flint_tc.json.disabled
===================================================================
--- testcases/heroes_unittests/Flint_tc.json.disabled prev run
+++ testcases/heroes_unittests/Flint_tc.json.disabled this run
@@ -0,0 +1,218 @@
+[
+ {
+ "test_id": "flint_tc_1",
+ "time": "2025-07-20 - 03:49",
+ "disabled_reason": "Historical 2025-07-20 reports use Flint's stochastic skill kit, not the current permanent bonuses. At that time Pyromaniac had a 20% burn proc and Immolation a 50% damage-taken proc (git 6b223c2f^:assets/hero_skills/Flint.json). Current extracted client skills 500214/500215/500216 instead grant infantry damage, all-troop attack and all-troop lethality (c2/data/client-schema-wip/config/skills.json). Preserve these outcomes as historical evidence; do not calibrate the current simulator against them.",
+ "attacker": {
+ "name": "daut",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 1000,
+ "lancer_t6": 500,
+ "marksman_t6": 500
+ },
+ "stats": {
+ "inf": {
+ "attack": 9.94,
+ "defense": 9.44,
+ "lethality": 1.75,
+ "health": 1.75
+ },
+ "lanc": {
+ "attack": 9.94,
+ "defense": 9.44,
+ "lethality": 1.75,
+ "health": 1.75
+ },
+ "mark": {
+ "attack": 10.43,
+ "defense": 9.44,
+ "lethality": 1.75,
+ "health": 1.75
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "viper",
+ "heroes": {
+ "Flint": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 100,
+ "lancer_t6": 50,
+ "marksman_t6": 50
+ },
+ "stats": {
+ "inf": {
+ "attack": 313.87,
+ "defense": 317.37,
+ "lethality": 107.24,
+ "health": 114.18
+ },
+ "lanc": {
+ "attack": 167.0,
+ "defense": 163.0,
+ "lethality": 110.31,
+ "health": 100.21
+ },
+ "mark": {
+ "attack": 174.75,
+ "defense": 168.25,
+ "lethality": 117.75,
+ "health": 104.74
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "sim_skills_used": {
+ "attacker": {
+ "MARKSMEN- Ranged Strike/1": "1 (22)"
+ },
+ "defender": {
+ "Flint- Pyromaniac/1": "10 (24)",
+ "Flint- Burning resolve/1": "1 (66)",
+ "Flint- Immolation/1": "34 (102)",
+ "INFANTRY- Master Brawler/1": "1 (2)",
+ "LANCERS- Charge/1": "1 (2)",
+ "MARKSMEN- Ranged Strike/1": "1 (18)"
+ }
+ },
+ "game_report_result": [
+ {
+ "attacker": 0,
+ "defender": 150
+ },
+ {
+ "attacker": 0,
+ "defender": 151
+ },
+ {
+ "attacker": 0,
+ "defender": 149
+ },
+ {
+ "attacker": 0,
+ "defender": 149
+ },
+ {
+ "attacker": 0,
+ "defender": 150
+ }
+ ]
+ },
+ {
+ "test_id": "flint_tc_2",
+ "time": "2025-07-20 - 04:07",
+ "disabled_reason": "Historical 2025-07-20 reports use Flint's stochastic skill kit, not the current permanent bonuses. At that time Pyromaniac had a 20% burn proc and Immolation a 50% damage-taken proc (git 6b223c2f^:assets/hero_skills/Flint.json). Current extracted client skills 500214/500215/500216 instead grant infantry damage, all-troop attack and all-troop lethality (c2/data/client-schema-wip/config/skills.json). Preserve these outcomes as historical evidence; do not calibrate the current simulator against them.",
+ "attacker": {
+ "name": "daut",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 3000,
+ "lancer_t6": 2000,
+ "marksman_t6": 2000
+ },
+ "stats": {
+ "inf": {
+ "attack": 9.94,
+ "defense": 9.44,
+ "lethality": 1.75,
+ "health": 1.75
+ },
+ "lanc": {
+ "attack": 9.94,
+ "defense": 9.44,
+ "lethality": 1.75,
+ "health": 1.75
+ },
+ "mark": {
+ "attack": 10.43,
+ "defense": 9.44,
+ "lethality": 1.75,
+ "health": 1.75
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "viper",
+ "heroes": {
+ "Flint": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 100,
+ "lancer_t6": 50,
+ "marksman_t6": 50
+ },
+ "stats": {
+ "inf": {
+ "attack": 313.87,
+ "defense": 317.37,
+ "lethality": 107.24,
+ "health": 114.18
+ },
+ "lanc": {
+ "attack": 167.0,
+ "defense": 163.0,
+ "lethality": 110.31,
+ "health": 100.21
+ },
+ "mark": {
+ "attack": 174.75,
+ "defense": 168.25,
+ "lethality": 117.75,
+ "health": 104.74
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "sim_skills_used": {
+ "attacker": {
+ "INFANTRY- Master Brawler/1": "1 (1)",
+ "LANCERS- Charge/1": "1 (5)",
+ "MARKSMEN- Ranged Strike/1": "1 (46)"
+ },
+ "defender": {
+ "Flint- Pyromaniac/1": "34 (100)",
+ "Flint- Burning resolve/1": "1 (155)",
+ "Flint- Immolation/1": "85 (245)",
+ "MARKSMEN- Ranged Strike/1": "1 (47)"
+ }
+ },
+ "game_report_result": [
+ {
+ "attacker": 5720,
+ "defender": 0
+ },
+ {
+ "attacker": 5756,
+ "defender": 0
+ },
+ {
+ "attacker": 5736,
+ "defender": 0
+ },
+ {
+ "attacker": 5715,
+ "defender": 0
+ },
+ {
+ "attacker": 5733,
+ "defender": 0
+ },
+ {
+ "attacker": 5736,
+ "defender": 0
+ }
+ ]
+ }
+]
\ No newline at end of file
Show raw per-run patches
Run A dirty state patch
diff --git a/simulator/config/hero_definitions/Ahmose.json b/simulator/config/hero_definitions/Ahmose.json
--- a/simulator/config/hero_definitions/Ahmose.json
+++ b/simulator/config/hero_definitions/Ahmose.json
@@ -102,7 +102,7 @@
},
"effects": {
"BladeOfLight/1": {
- "type": "active.hero.damage.up",
+ "type": "extra_skill_attack",
"value": [
12,
24,
@@ -115,11 +115,12 @@
"infantry"
]
},
- "duration": {
- "attacks": {
- "count": 1
+ "trigger_damage_jobs": [
+ {
+ "source": "use.source",
+ "target": "use.target"
}
- }
+ ]
},
"BladeOfLight/2": {
"type": "active.hero.damageTaken.up",
diff --git a/simulator/config/hero_definitions/Gordon.json b/simulator/config/hero_definitions/Gordon.json
--- a/simulator/config/hero_definitions/Gordon.json
+++ b/simulator/config/hero_definitions/Gordon.json
@@ -22,8 +22,8 @@
100
],
"units": {
- "applies_to": "trigger.source",
- "applies_vs": "trigger.target"
+ "applies_to": "trigger.source",
+ "applies_vs": "trigger.target"
},
"duration": {
"attacks": {
@@ -61,6 +61,7 @@
"effects": {
"ChemicalTerror/1": {
"type": "active.hero.damage.up",
+ "applies_to_damage_kinds": ["normal"],
"value": [
30,
60,
@@ -75,7 +76,8 @@
},
"duration": {
"turns": {
- "count": 1
+ "count": 1,
+ "delay": 1
}
},
"same_effect_stacking": "max"
@@ -99,6 +101,28 @@
}
},
"same_effect_stacking": "max"
+ },
+ "ChemicalTerror/3": {
+ "type": "active.hero.damage.up",
+ "applies_to_damage_kinds": ["skill"],
+ "value": [
+ 30,
+ 60,
+ 90,
+ 120,
+ 150
+ ],
+ "units": {
+ "applies_to": [
+ "lancer"
+ ]
+ },
+ "duration": {
+ "turns": {
+ "count": 1
+ }
+ },
+ "same_effect_stacking": "max"
}
}
},
diff --git a/simulator/config/hero_definitions/README.md b/simulator/config/hero_definitions/README.md
--- a/simulator/config/hero_definitions/README.md
+++ b/simulator/config/hero_definitions/README.md
@@ -459,11 +459,13 @@
The extra-attack effect is charged once for the parent normal attack if at least one of its jobs runs. If its `value` is non-positive, every job lacks living source/target troops, or every job is skipped because its target is already exhausted, the effect is not charged and can remain available.
-Immediate and delayed hits share the same generation and delivery routines. Generation uses reusable scratch and the prepared modifier-group index to calculate non-shield damage. Delivery selects applicable shields from a small live list, consumes protection, and applies the current target-troop cap. Shield activation and expiry use the ordinary effect scheduler; shields retain group identity for stacking and dependencies but are excluded from the modifier job-shape lists.
+Immediate and delayed hits share bucket evaluation, shield settlement, and target caps. Immediate hits calculate and consume their modifiers together. Delayed hits instead separate captured contributions from attack-limited target modifiers.
-When an `extra_skill_attack` is materialized as a carrier's `trigger_effects` child, its generated damage is retained on that child ActiveEffect. Fast and standard modes retain scalar calculation state and the recording context; only trace mode copies the full bucket factors. Shields are not selected, advanced, or consumed during generation. The child can deliver its damage only when a later normal attack matches its resolved source and target scope, after that normal hit has resolved. Delivery uses the generation-time health conversion factor for shield-budget conversion without recalculating the damage. If the matching attack never occurs during the child's duration, the pending damage expires without consuming shields.
+When an `extra_skill_attack` is materialized as a carrier's `trigger_effects` child, its generated damage is retained on that child ActiveEffect. All modes retain owned dynamic factors and captured static terms so delivery can combine modifiers without recalculating the source's troop strength. Attack-limited target modifiers and shields are not selected, advanced, or consumed during capture. When a later normal attack matches the child's source and target scope, pending damage lands before that attack is declared and before it creates new effects. Delivery applies the currently eligible attack-limited target modifiers to the captured buckets, consumes their uses, and settles live shields and the current target-troop cap. If no matching attack occurs during the child's duration, the pending damage expires without consuming those modifiers or shields.
-Renee's Nightmare Trace uses this form with a one-turn delay: the even-round Lancer attack locks the target and calculates the damage, then the following odd-round Lancer attack delivers it. The precise capture ordering and treatment of defensive state are provisional reverse-engineering assumptions, not confirmed game mechanics.
+Renee's Nightmare Trace uses this form with a one-turn delay: the even-round Lancer attack locks the target and calculates the damage, then a matching allied attack on the following odd round delivers it. Its child uses `self.any` for delivery eligibility; the retained job still uses the original Lancer source and locked target, allowing delivery after the Lancers die. The precise capture ordering and treatment of defensive state remain provisional reverse-engineering assumptions.
+
+Renee's Dreamslice mark increases both normal and skill damage taken by the marked target. Dreamcatcher's Lancer-specific increase remains restricted to normal damage.
All jobs emitted by one use read the parent extra-attack effect's same current `value`; its use/evolution is charged only after those jobs finish. Other attack-limited **modifier** effects are different: each generated skill job is a separate modifier use, and modifiers are charged after each job, so a one-job modifier can expire before the second target in the same extra attack is calculated.
diff --git a/simulator/config/hero_definitions/Renee.json b/simulator/config/hero_definitions/Renee.json
--- a/simulator/config/hero_definitions/Renee.json
+++ b/simulator/config/hero_definitions/Renee.json
@@ -24,7 +24,7 @@
"type": "extra_skill_attack",
"value": [40, 80, 120, 160, 200],
"units": {
- "applies_to": "parent.use.source",
+ "applies_to": "self.any",
"applies_vs": "parent.use.target"
},
"trigger_damage_jobs": [
@@ -96,7 +96,6 @@
"trigger_effects": {
"Dreamslice/1": {
"type": "active.hero.damageTaken.up",
- "applies_to_damage_kinds": ["normal"],
"value": [15, 30, 45, 60, 75],
"units": {
"applies_to": "parent.use.target",
diff --git a/simulator/src/damage.test.ts b/simulator/src/damage.test.ts
--- a/simulator/src/damage.test.ts
+++ b/simulator/src/damage.test.ts
@@ -107,6 +107,9 @@
group = {
ordinal: groups.length,
bucketIndex: DYNAMIC_BUCKETS.findIndex((definition) => definition.name === activeEffect.intent.type),
+ attackLimitedTakerModifier: activeEffect.kind === "modifier" &&
+ activeEffect.duration.attacks !== undefined &&
+ DYNAMIC_BUCKETS.find(definition => definition.name === activeEffect.intent.type)?.jobSide === "taker",
sameEffectStacking: activeEffect.sameEffectStacking
};
byResolvedGroup.set(key, group);
diff --git a/simulator/src/damage.ts b/simulator/src/damage.ts
--- a/simulator/src/damage.ts
+++ b/simulator/src/damage.ts
@@ -115,13 +115,15 @@
minInitialArmy?: number;
}
-/** Borrowed during immediate delivery; retainGeneratedDamage owns delayed state. */
+/** Immediate generation borrows scratch; captured generation owns its factors and static terms. */
export interface GeneratedDamage {
factors?: Float64Array;
recording: DamageJobRecorder;
armyTerm: number;
damageBeforeOffsets: number;
healthFactor: number;
+ capturedDealerFactor?: number;
+ capturedTakerFactor?: number;
}
export function generateDamageJob(
@@ -159,17 +161,75 @@
};
}
+/** Snapshot source/static/turn-based contributions; attack-limited taker modifiers wait for landing. */
+export function generateCapturedDamageJob(
+ job: DamageJob,
+ fighters: Record<SideId, ResolvedFighter>,
+ options: DamageJobOptions
+): GeneratedDamage {
+ const recording = options.recorder.startDamageJob();
+ const buckets = options.scratch ? resetDamageScratch(options.scratch) : createNumericDamageBuckets();
+ const usedEffects = options.usedEffects ?? [];
+ const armyTerm = populateSourceBuckets(job, fighters, options, buckets);
+ applyPartitionedBucketEffects(job, options.effectIndex, buckets, recording, usedEffects, options.primaryUsedEffects ?? usedEffects, false);
+ const capturedDealerFactor = bucketSetValue(options.staticDamageProfile[job.dealerSide][job.dealerUnit].dealerFactor);
+ const capturedTakerFactor = bucketSetValue(options.staticDamageProfile[job.takerSide][job.takerUnit].takerFactor);
+ const expression = DYNAMIC_EXPRESSIONS[job.kind];
+ const damageBeforeOffsets = multiplySlots(1, buckets.factors, expression.numeratorSlots) /
+ multiplySlots(1, buckets.factors, expression.denominatorSlots) *
+ capturedDealerFactor * capturedTakerFactor * bucketSetValue(DAMAGE_SCALE_BUCKET);
+ return {
+ factors: options.scratch ? buckets.factors.slice() : buckets.factors,
+ recording,
+ armyTerm,
+ damageBeforeOffsets,
+ healthFactor: dynamicHealthFactor(buckets.factors),
+ capturedDealerFactor,
+ capturedTakerFactor
+ };
+}
+
+/** Add current taker attack budgets to the original buckets, then settle current shields and caps. */
+export function deliverCapturedDamageJob(
+ job: DamageJob,
+ generated: GeneratedDamage,
+ options: DamageJobOptions
+): DamageResult {
+ const buckets = options.scratch ? resetDamageScratch(options.scratch) : createNumericDamageBuckets();
+ buckets.factors.set(generated.factors!);
+ const usedEffects = options.usedEffects ?? [];
+ applyPartitionedBucketEffects(job, options.effectIndex, buckets, generated.recording, usedEffects, options.primaryUsedEffects ?? usedEffects, true);
+ const expression = DYNAMIC_EXPRESSIONS[job.kind];
+ // Re-evaluate, rather than dividing by captured factors: a captured bucket may be zero.
+ const damageBeforeOffsets = multiplySlots(1, buckets.factors, expression.numeratorSlots) /
+ multiplySlots(1, buckets.factors, expression.denominatorSlots) *
+ generated.capturedDealerFactor! * generated.capturedTakerFactor! * bucketSetValue(DAMAGE_SCALE_BUCKET);
+ return finishDamageJob(job, generated, options, buckets, damageBeforeOffsets, dynamicHealthFactor(buckets.factors));
+}
+
export function deliverDamageJob(
job: DamageJob,
generated: GeneratedDamage,
options: DamageJobOptions
): DamageResult {
- const { recording, armyTerm, damageBeforeOffsets, healthFactor } = generated;
+ const { damageBeforeOffsets, healthFactor } = generated;
const buckets = options.scratch ?? createNumericDamageBuckets();
if (generated.factors !== buckets.factors) {
resetDamageScratch(buckets);
if (generated.factors) buckets.factors.set(generated.factors);
}
+ return finishDamageJob(job, generated, options, buckets, damageBeforeOffsets, healthFactor);
+}
+
+function finishDamageJob(
+ job: DamageJob,
+ generated: GeneratedDamage,
+ options: DamageJobOptions,
+ buckets: NumericDamageBuckets,
+ damageBeforeOffsets: number,
+ healthFactor: number
+): DamageResult {
+ const { recording, armyTerm } = generated;
const usedEffects = options.usedEffects ?? [];
const primaryUsedEffects = options.primaryUsedEffects ?? usedEffects;
const offsetDamage = applyShields(job, options.effectIndex, buckets, recording, damageBeforeOffsets, healthFactor, usedEffects, primaryUsedEffects);
@@ -188,15 +248,7 @@
usedEffects: ActiveEffect[],
primaryUsedEffects: ActiveEffect[]
): number {
- // Fractional casualties carry between rounds, but every positive remainder is
- // still one living troop and contributes fully to the next attack.
- const dealerTroops = ceilIgnoringFloatResidue(
- Math.max(0, job.roundStartTroops[job.dealerSide][job.dealerUnit] ?? 0)
- );
- const initialArmy = options.minInitialArmy ?? minInitialArmy(fighters);
- const armyTerm = Math.sqrt(dealerTroops) * Math.sqrt(initialArmy);
- applyDynamicDamageBucketValue(buckets, "troops.count", armyTerm);
- applyDynamicDamageBucketValue(buckets, "source.multiplier", job.sourceMultiplier ?? 1);
+ const armyTerm = populateSourceBuckets(job, fighters, options, buckets);
const jobSlot = damageJobSlot(job);
applyBucketEffects(
@@ -212,6 +264,47 @@
return armyTerm;
}
+function populateSourceBuckets(
+ job: DamageJob,
+ fighters: Record<SideId, ResolvedFighter>,
+ options: DamageJobOptions,
+ buckets: NumericDamageBuckets
+): number {
+ // Fractional casualties carry between rounds, but every positive remainder is
+ // still one living troop and contributes fully to the next attack.
+ const dealerTroops = ceilIgnoringFloatResidue(
+ Math.max(0, job.roundStartTroops[job.dealerSide][job.dealerUnit] ?? 0)
+ );
+ const initialArmy = options.minInitialArmy ?? minInitialArmy(fighters);
+ const armyTerm = Math.sqrt(dealerTroops) * Math.sqrt(initialArmy);
+ applyDynamicDamageBucketValue(buckets, "troops.count", armyTerm);
+ applyDynamicDamageBucketValue(buckets, "source.multiplier", job.sourceMultiplier ?? 1);
+ return armyTerm;
+}
+
+function dynamicHealthFactor(factors: Float64Array): number {
+ return factors[DYNAMIC_BUCKET_INDEX["active.hero.health.up"]] *
+ factors[DYNAMIC_BUCKET_INDEX["active.troop.health.up"]] /
+ factors[DYNAMIC_BUCKET_INDEX["active.hero.health.down"]] /
+ factors[DYNAMIC_BUCKET_INDEX["active.troop.health.down"]];
+}
+
+function applyPartitionedBucketEffects(
+ job: DamageJob,
+ index: EffectIndex,
+ buckets: NumericDamageBuckets,
+ recording: DamageJobRecorder,
+ usedEffects: ActiveEffect[],
+ primaryUsedEffects: ActiveEffect[],
+ attackLimitedTakerModifier: boolean
+): void {
+ const jobSlot = damageJobSlot(job);
+ for (const group of index.damageGroupsByJobShape[jobSlot]) {
+ if (group.attackLimitedTakerModifier !== attackLimitedTakerModifier) continue;
+ applyEffectGroup(group, index.liveEffectsByGroup, job.round, jobSlot, buckets, recording, usedEffects, primaryUsedEffects, attackLimitedTakerModifier);
+ }
+}
+
function applyBucketEffects(
groups: ActiveEffectGroup[],
@@ -236,10 +329,11 @@
buckets: NumericDamageBuckets,
recording: DamageJobRecorder,
usedEffects: ActiveEffect[],
- primaryUsedEffects: ActiveEffect[]
+ primaryUsedEffects: ActiveEffect[],
+ attackLimitedTakerModifier?: boolean
): void {
const dependency = group.requiredGroupOrdinalsByJobShape;
- if (dependency && !requiredEffectIsApplicable(dependency, liveEffectsByGroup, round, jobSlot)) return;
+ if (dependency && !requiredEffectIsApplicable(dependency, liveEffectsByGroup, round, jobSlot, attackLimitedTakerModifier)) return;
const effects = liveEffectsByGroup[group.ordinal];
if (effects.length === 0) return;
if (group.sameEffectStacking !== "max") {
@@ -323,12 +417,15 @@
dependency: Array<number[] | undefined>,
liveEffectsByGroup: ActiveEffect[][],
round: number,
- jobSlot: number
+ jobSlot: number,
+ attackLimitedTakerModifier?: boolean
): boolean {
const requiredOrdinals = dependency[jobSlot];
if (!requiredOrdinals) return false;
for (const ordinal of requiredOrdinals) {
for (const effect of liveEffectsByGroup[ordinal]) {
+ if (attackLimitedTakerModifier !== undefined &&
+ effect.effectGroup!.attackLimitedTakerModifier !== attackLimitedTakerModifier) continue;
if (isEffectAttackReady(effect) && effect.getCurrentValue(round) !== 0) return true;
}
}
diff --git a/simulator/src/effectIndex.test.ts b/simulator/src/effectIndex.test.ts
--- a/simulator/src/effectIndex.test.ts
+++ b/simulator/src/effectIndex.test.ts
@@ -122,6 +122,7 @@
group = {
ordinal: groups.length,
bucketIndex: 0,
+ attackLimitedTakerModifier: false,
sameEffectStacking: effect.sameEffectStacking
};
byResolvedGroup.set(key, group);
diff --git a/simulator/src/extraAttacks.ts b/simulator/src/extraAttacks.ts
--- a/simulator/src/extraAttacks.ts
+++ b/simulator/src/extraAttacks.ts
@@ -9,7 +9,14 @@
} from "./types";
import { UNIT_TYPES, unitMaskHas, unitsFromMask } from "./types";
import { advanceEffectAttackDelay } from "./effects";
-import { generateDamageJob, deliverDamageJob, retainGeneratedDamage, ceilIgnoringFloatResidue, type DamageJobOptions } from "./damage";
+import {
+ generateDamageJob,
+ deliverDamageJob,
+ generateCapturedDamageJob,
+ deliverCapturedDamageJob,
+ ceilIgnoringFloatResidue,
+ type DamageJobOptions
+} from "./damage";
import { normalizeUnitType } from "./normalize";
import {
capJobToRemainingTarget,
@@ -23,14 +30,14 @@
type Runtime
} from "./runtime";
-// Spawn, calculate, and charge extra attacks riding on one normal attack.
-// Returns the finalized kills from every generated job in this normal attack's
-// cluster. A triggered child can instead carry damage captured in an earlier round;
-// shields are settled only when the child's locked normal attack matches.
+// Pending jobs land before the matching normal attack is declared; immediate jobs
+// run after its damage. Each effect participates in only one phase, so its attack
+// delay advances once per matching normal attack. Only immediate skill kills feed
+// that normal attack's deferred formulas.
// The effect is charged one use only when at least one of its own jobs actually ran.
-// Spawning reads only round-start state and the effect's own gates, so running earlier
-// effects' jobs first cannot change what later effects spawn.
+// Snapshot eligible effects before delivery because charging may expire them.
export function processExtraAttacks(
+ phase: "pending" | "immediate",
normalAttack: DamageJob,
intent: AttackIntent,
runtime: Runtime,
@@ -47,23 +54,32 @@
let skillKills = 0;
// Snapshot the applicable effects: charging an effect below may expire it out of the live index.
const effects = runtime.effectIndex.extraAttacks.filter(
- (effect) => extraAttackEffectAppliesToNormalAttack(effect, normalAttack) && advanceEffectAttackDelay(effect)
+ (effect) =>
+ (phase === "pending" ? (effect.pendingDamageJobs?.length ?? 0) > 0 : effect.pendingDamageJobs === undefined) &&
+ extraAttackEffectAppliesToNormalAttack(effect, normalAttack) &&
+ advanceEffectAttackDelay(effect)
);
for (const effect of effects) {
const sourceEffectId = effect.source.effectId ?? effect.intent.id;
let processedJobCount = 0;
let firstProcessedJob: DamageJob | undefined;
if (effect.pendingDamageJobs) {
- for (const pending of effect.pendingDamageJobs) {
+ const pendingJobs = effect.pendingDamageJobs;
+ let retainedJobCount = 0;
+ for (const pending of pendingJobs) {
const deliveryJob: DamageJob = {
...pending.job,
round,
calculationRound: pending.job.round,
roundStartTroops
};
- if (loopOptions.capRoundKills && targetExhausted(deliveryJob, roundStartTroops, roundTargetDamage)) continue;
+ if (loopOptions.capRoundKills && targetExhausted(deliveryJob, roundStartTroops, roundTargetDamage)) {
+ pendingJobs[retainedJobCount++] = pending;
+ continue;
+ }
recorder.recordScheduledDamageJob(deliveryJob);
- const result = deliverDamageJob(deliveryJob, pending.generated, damageJobOptions);
+ const result = deliverCapturedDamageJob(deliveryJob, pending.generated, damageJobOptions);
+ if (deliveryJob.kind === "skill") recorder.recordSkillDamageJob(deliveryJob, effect);
if (loopOptions.capRoundKills) {
capJobToRemainingTarget(result, deliveryJob, roundStartTroops, roundTargetDamage, recorder);
} else if (loopOptions.capJobKills) {
@@ -75,10 +91,15 @@
}
results.push({ job: deliveryJob, result, intent });
totalKills += result.kills;
+ if (deliveryJob.kind === "skill") {
+ skillKills += result.kills;
+ runtime.extraSkillAttackJobsByEffect[sourceEffectId] = (runtime.extraSkillAttackJobsByEffect[sourceEffectId] ?? 0) + 1;
+ }
processedJobCount += 1;
firstProcessedJob ??= deliveryJob;
chargeUsedEffectsForJob(runtime, deliveryJob, recorder);
}
+ pendingJobs.length = retainedJobCount;
} else {
for (const definition of effect.triggerDamageJobs ?? []) {
const sources = resolveTriggerJobSelector(definition.source, "source", effect, normalAttack, roundStartTroops);
@@ -161,7 +182,7 @@
sourceEffectId: effect.source.effectId ?? effect.intent.id,
sourceMultiplier: multiplier
};
- const generated = retainGeneratedDamage(generateDamageJob(job, fighters, damageJobOptions));
+ const generated = generateCapturedDamageJob(job, fighters, damageJobOptions);
pending.push({ job, generated });
chargeUsedEffectsForJob(runtime, job, damageJobOptions.recorder);
}
diff --git a/simulator/src/runtimeSkills.ts b/simulator/src/runtimeSkills.ts
--- a/simulator/src/runtimeSkills.ts
+++ b/simulator/src/runtimeSkills.ts
@@ -142,7 +142,9 @@
const group: ActiveEffectGroup = {
ordinal: effectGroups.length,
bucketIndex: damageBucketIndex(definition.name),
- sameEffectStacking: effect.sameEffectStacking
+ sameEffectStacking: effect.sameEffectStacking,
+ attackLimitedTakerModifier: effect.kind === "modifier" &&
+ effect.duration.attacks !== undefined && definition.jobSide === "taker"
};
groupMetadata.set(group, {
ownerSide: skill.side,
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
@@ -21,6 +21,71 @@
return runPrepared(prepareBattle(input, config), undefined, options);
}
+test("Gordon suppresses on third turns and boosts normal Lancer damage on the following turns", () => {
+ const result = runOnce({
+ maxRounds: 7,
+ attacker: {
+ troops: { lancer_t6: 1000 },
+ heroes: { Gordon: { skill_1: 2, skill_2: 2, skill_3: 2 } }
+ },
+ defender: { troops: { lancer_t6: 1000 } }
+ }, loadSimulatorConfig(), { mode: "standard" });
+
+ assert.deepEqual(result.attacks
+ .filter((attack) => attack.appliedEffects?.some((effect) => effect.effectId === "ChemicalTerror/1"))
+ .map((attack) => attack.round), [4, 7]);
+ assert.deepEqual(result.attacks
+ .filter((attack) => attack.appliedEffects?.some((effect) => effect.effectId === "ChemicalTerror/2"))
+ .map((attack) => attack.round), [3, 6]);
+});
+
+test("Gordon boosts Wayne skill damage on Chemical Terror's triggering turn", () => {
+ const [input] = JSON.parse(readFileSync(new URL("../../testcases/emulator_verified/gordon_interactions_20260925/gordon_wayne_1200l_vs_4800l.json", import.meta.url), "utf8"));
+ assert.equal(signedRemainingScore(runOnce(input, loadSimulatorConfig())), -313);
+});
+
+test("Ahmose Blade of Light deals separate skill damage with Bradley and against Wu Ming", () => {
+ const config = loadSimulatorConfig();
+ for (const [path, survivors] of [
+ ["ahmose_bradley_1000i_vs_3700i", -77],
+ ["ahmose_1000i_vs_wuming_1000i", -82]
+ ] as const) {
+ const [input] = JSON.parse(readFileSync(new URL(`../../testcases/emulator_verified/ahmose_isolation_20260925/${path}.json`, import.meta.url), "utf8"));
+ assert.equal(signedRemainingScore(runOnce(input, config)), survivors, path);
+ }
+});
+
+test("Renee Dreamslice amplifies allied skill damage against the marked target", () => {
+ const [input] = JSON.parse(readFileSync(new URL("../../testcases/emulator_verified/ahmose_gordon_renee/ahmose_renee_vs_jasser_mark_kind.json", import.meta.url), "utf8"));
+ assert.equal(signedRemainingScore(runOnce(input, loadSimulatorConfig())), 937);
+});
+
+test("Renee's captured Dream Mark survives the loss of its Lancer source", () => {
+ const [input] = JSON.parse(readFileSync(new URL("../../testcases/emulator_verified/renee_isolation_20260925/renee_mark_source_death.json", import.meta.url), "utf8"));
+ assert.equal(signedRemainingScore(runOnce(input, loadSimulatorConfig())), 184);
+});
+
+test("Dream Mark capture leaves next-hit vulnerability for Marksmen and delivery precedes the next ordinary attack", () => {
+ const input: BattleInput = {
+ maxRounds: 3,
+ attacker: {
+ troops: { lancer_t6: 100000, marksman_t6: 100000 },
+ heroes: { Renee: { skill_1: 4 }, Gwen: { skill_1: 3 } }
+ },
+ defender: { troops: { infantry_t6: 1000000 }, heroes: {} }
+ };
+ const result = runOnce(input, loadSimulatorConfig(), { mode: "trace" });
+ const marksmanHit = result.attacks.find(attack =>
+ attack.round === 2 && attack.dealerSide === "attacker" && attack.dealerUnit === "marksman"
+ )!;
+ assert.ok(marksmanHit.appliedEffects?.some(effect => effect.effectId === "EagleVision/1"));
+ const nextTurn = result.attacks.filter(attack => attack.round === 3 && attack.dealerSide === "attacker");
+ assert.equal(nextTurn[0].sourceEffectId, "NightmareTrace/1");
+ assert.ok(nextTurn[0].appliedEffects?.some(effect => effect.effectId === "EagleVision/1"));
+ assert.equal(nextTurn[1].sourceEffectId, undefined);
+ assert.equal(nextTurn[1].appliedEffects?.some(effect => effect.effectId === "EagleVision/1"), false);
+});
+
test("delayed damage settles shields on arrival in recorded Gatot battles", () => {
const config = loadSimulatorConfig();
for (const [path, survivors] of [
diff --git a/simulator/src/simulator.ts b/simulator/src/simulator.ts
--- a/simulator/src/simulator.ts
+++ b/simulator/src/simulator.ts
@@ -299,6 +299,11 @@
continue;
}
+ const pendingAttacks = processExtraAttacks("pending", job, intent, runtime, fighters, damageJobOptions, roundTargetDamage, loopOptions, results);
+ if (loopOptions.scoreSide && job.dealerSide === loopOptions.scoreSide.dealerSide && job.takerSide === loopOptions.scoreSide.takerSide) {
+ score += pendingAttacks.totalKills;
+ }
+
const matchingTriggerSkills = runtime.skills.attackDeclaredByJobShape[
damageJobShapeSlot("normal", intent.dealerSide, intent.dealerUnit, intent.takerSide, intent.takerUnit)
];
@@ -328,7 +333,7 @@
captureTriggeredExtraDamage(triggeredChildren, job, fighters, damageJobOptions, runtime);
advanceNormalAttackCounters(intent, runtime);
- const extraAttacks = processExtraAttacks(job, intent, runtime, fighters, damageJobOptions, roundTargetDamage, loopOptions, results);
+ const extraAttacks = processExtraAttacks("immediate", job, intent, runtime, fighters, damageJobOptions, roundTargetDamage, loopOptions, results);
if (loopOptions.scoreSide && job.dealerSide === loopOptions.scoreSide.dealerSide && job.takerSide === loopOptions.scoreSide.takerSide) {
score += extraAttacks.totalKills;
}
diff --git a/simulator/src/types.ts b/simulator/src/types.ts
--- a/simulator/src/types.ts
+++ b/simulator/src/types.ts
@@ -290,6 +290,8 @@
ordinal: number;
bucketIndex: number;
sameEffectStacking: SameEffectStacking;
+ /** Taker-side attack budgets are sampled when captured damage lands, not when it is generated. */
+ attackLimitedTakerModifier: boolean;
// Prepared dependency lookup indexed by damage-job shape. An empty slot means the
// required effect cannot apply to that job, so this group must not contribute.
requiredGroupOrdinalsByJobShape?: Array<number[] | undefined>;
Run B dirty state patch
diff --git a/testcases/emulator_verified/attdef_test_minxxx_attacks_nc.json b/testcases/emulator_verified/attdef_test_minxxx_attacks_nc.json
--- a/testcases/emulator_verified/attdef_test_minxxx_attacks_nc.json
+++ b/testcases/emulator_verified/attdef_test_minxxx_attacks_nc.json
@@ -65,7 +65,7 @@
},
"mark": {
"attack": 83.3,
- "defense": 0.0,
+ "defense": 69.9,
"lethality": 37.6,
"health": 29.4
}