WOS Sim
← Back to Runs

Compare Runs

A (baseline)
002e2b2e-57c7f6ec1ee08/09/2026, 08:53:57
B (current)
9fa2b4ec-e2e2b3f792825/09/2026, 09:20:40
Avg Error A0.20%
Avg Error B0.14%
Δ Avg Error-0.06%
Changed41
Improved28
Regressed13
Added113
Retired0

Testcase Delta

414 / 414
FileCase#AdjA%B%Δ%Status
testcases/emulator_verified/gwen_blastmaster_small_timing_550m_vs_810i_45l_45m.jsongwen_blastmaster_small_timing_550m_vs_810i_45l_45m0-0.01%6.83%0.00%-6.83%improved
testcases/emulator_verified/hendrik_small_timing_250m_vs_150i_60l_60m.jsonhendrik_small_timing_250m_vs_150i_60l_60m0—-1.92%0.00%-1.92%improved
testcases/emulator_verified/gwen_blastmaster_cadence_800m_vs_1200i_100l_100m.jsongwen_blastmaster_cadence_800m_vs_1200i_100l_100m0—1.27%0.00%-1.27%improved
testcases/emulator_verified/ambusher_logan_renee_vs_logan_patrick_nc.jsonambusher_logan_renee_vs_logan_patrick_nc0—-6.05%-5.37%-0.68%improved
testcases/emulator_verified/gordon_wayne_s1_extra_overlap_nc.jsongordon_wayne_s1_extra_overlap_nc0+0.023%0.63%0.00%-0.63%improved
testcases/emulator_verified/hendrik_dragons_heir_250m_vs_400i_100l_100m.jsonhendrik_dragons_heir_250m_vs_400i_100l_100m0—-0.59%0.00%-0.59%improved
testcases/emulator_verified/wayne_mixed_solo.jsonwayne_mixed_solo0—-0.79%-0.31%-0.48%improved
testcases/emulator_verified/bahiti_norah_combo_nc.jsonbahiti_norah_combo_nc0—-2.83%-2.44%-0.39%improved
testcases/emulator_verified/gwen_blastmaster_fanout_800m_vs_600_each.jsongwen_blastmaster_fanout_800m_vs_600_each0—0.35%0.00%-0.35%improved
testcases/emulator_verified/norah_zinman_combo_nc.jsonnorah_zinman_combo_nc0—0.24%0.51%+0.27%regressed
testcases/emulator_verified/sergey_norah_zinman_combo_nc.jsonsergey_norah_zinman_combo_nc0—-1.01%-0.74%-0.27%improved
testcases/emulator_verified/wayne_mixed_solo.jsonwayne_mixed_solo1—-2.05%-1.79%-0.26%improved
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.22%0.00%-0.22%improved
testcases/emulator_verified/gordon_wayne_s1_extra_overlap_lvl2.jsongordon_wayne_s1_extra_overlap_nc0-0.05%0.16%0.00%-0.16%improved
testcases/emulator_verified/norah_solo.jsonnorah_solo0—-0.19%-0.03%-0.16%improved
testcases/emulator_verified/norah_s2_splash_A.jsonnorah_s2_splash_A0—0.21%0.35%+0.14%regressed
testcases/emulator_verified/wos444_gordon_s22_all_enemy_damage_dealt_hero_nc.jsonwos444_gordon_s22_all_enemy_damage_dealt_hero_nc0—-0.17%-0.06%-0.11%improved
testcases/emulator_verified/renee_wayne_mixed_510_vs_888.jsonrenee_wayne_mixed_510_vs_8880—-0.26%-0.16%-0.10%improved
testcases/emulator_verified/ahmose_solo_nc.jsonahmose_solo0—0.09%0.00%-0.09%improved
testcases/emulator_verified/gwen_solo_nc.jsongwen_solo0+0.025%0.09%0.00%-0.09%improved
testcases/emulator_verified/renee_gwen_bucket_retry_nc.jsonrenee_gwen_bucket_retry_nc0-0.05%0.00%0.07%+0.07%regressed
testcases/3-testcases_mixed-heroes-not-verified.jsonno_heroes_t6_mixed_2x_10-0.005%0.00%0.06%+0.06%regressed
testcases/gatot_verified/s2-health-1.1-5100-vs-1000.jsons2-health-1.1-5100-vs-10000+0.005%0.00%-0.05%+0.05%regressed
testcases/emulator_verified/wayne_reina_combo.jsonwayne_reina_combo0—-0.36%-0.32%-0.04%improved
testcases/gatot_verified/s2-health-1.1-5050-vs-1000.jsons2-health-1.1-5050-vs-10000+0.005%0.00%-0.03%+0.03%regressed
testcases/heroes_unittests/Flint_tc.jsonflint_tc_21-0.005%0.38%0.41%+0.03%regressed
testcases/emulator_verified/gwen_norah_combo_nc.jsongwen_norah_combo_nc0—0.55%0.57%+0.02%regressed
testcases/emulator_verified/wayne_reina_wuming_combo.jsonwayne_reina_wuming_combo0—0.18%0.20%+0.02%regressed
testcases/gatot_verified/s10-bradley-1000-inf-125-marksman-vs-5000-inf.jsons10-bradley-1000-inf-125-marksman-vs-5000-inf0-0.019%-0.02%0.00%-0.02%improved
testcases/gatot_verified/s2-health-1.1-4950-vs-1000.jsons2-health-1.1-4950-vs-10000+0.005%0.00%-0.02%+0.02%regressed
testcases/gatot_verified/s2-health-1.1-5000-vs-1000.jsons2-health-1.1-5000-vs-10000+0.005%0.00%-0.02%+0.02%regressed
testcases/3-testcases_mixed-heroes-not-verified.jsonno_heroes_t6_mixed_2x_32+0.005%-0.39%-0.40%+0.01%regressed
testcases/emulator_verified/alonso_attacker_600_all.jsonalonso_attacker_600_all0—-0.15%-0.14%-0.01%improved
testcases/emulator_verified/alonso_norah_combo.jsonalonso_norah_combo1—-0.27%-0.26%-0.01%improved
testcases/emulator_verified/alonso_norah_combo.jsonalonso_norah_combo2—-0.18%-0.17%-0.01%improved
testcases/emulator_verified/alonso_solo.jsonalonso_solo0—2.96%2.95%-0.01%improved
testcases/emulator_verified/alonso_solo_v2.jsonalonso_solo_v21—-1.80%-1.79%-0.01%improved
testcases/emulator_verified/alonso_vs_alonso.jsonalonso_vs_alonso0—-0.08%-0.07%-0.01%improved
testcases/emulator_verified/norah_greg_combo.jsonnorah_greg_combo0—-0.01%0.00%-0.01%improved
testcases/emulator_verified/norah_hector_zinman_combo.jsonnorah_hector_zinman_combo0—0.06%0.07%+0.01%regressed
testcases/heroes_unittests/Reina_tc.jsonreina_tc_42—-0.58%-0.57%-0.01%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.004%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.005%0.00%0.00%0.00%unchanged
testcases/1-testcases_no-heroes_t6_single-type_nc.jsonno_heroes_single_t6__90+0.005%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_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_no_infantry_240l_vs_125i.jsonahmose_no_infantry_240l_vs_125i0—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_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_solo_v2.jsonalonso_solo_v20—2.21%2.21%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_minxxx_attacks_nc.jsonattdef_test_minxxx_attacks_nc0-0.05%0.08%0.08%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_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/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/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/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/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_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/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_hendrik_defense_bucket_nc.jsonrenee_hendrik_defense_bucket_nc0—0.00%0.00%0.00%unchanged
testcases/emulator_verified/renee_solo_nc.jsonrenee_solo_nc0—0.00%0.00%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_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_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.025%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/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/s3-5000-inf-vs-1000-inf-10-lancer.jsons3-5000-inf-vs-1000-inf-10-lancer0-0.05%7.04%7.04%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/Flint_tc.jsonflint_tc_10+0.005%-0.01%-0.01%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_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_blade_categories/ahmose_vs_jasser_01.jsonahmose_vs_jasser_010——0.00%—added
testcases/emulator_verified/ahmose_blade_categories/ahmose_vs_wu_ming.jsonahmose_vs_wu_ming0——0.00%—added
testcases/emulator_verified/ahmose_blade_categories/ahmose_wayne_vs_jasser.jsonahmose_wayne_vs_jasser0——0.00%—added
testcases/emulator_verified/ahmose_blade_categories/jasser_vs_ahmose_01.jsonjasser_vs_ahmose_010——0.00%—added
testcases/emulator_verified/ahmose_blade_categories/jasser_vs_ahmose_wayne.jsonjasser_vs_ahmose_wayne0——0.00%—added
testcases/emulator_verified/ahmose_blade_categories/wu_ming_vs_ahmose.jsonwu_ming_vs_ahmose0——0.00%—added
testcases/emulator_verified/ahmose_bucket_interactions/ahmose_hendrik_vs_jasser.jsonahmose_hendrik_vs_jasser0——0.00%—added
testcases/emulator_verified/ahmose_bucket_interactions/ahmose_jasser_vs_jasser.jsonahmose_jasser_vs_jasser0——0.00%—added
testcases/emulator_verified/ahmose_bucket_interactions/ahmose_reina_vs_jasser.jsonahmose_reina_vs_jasser0——-0.12%—added
testcases/emulator_verified/ahmose_bucket_interactions/ahmose_seo_yoon_vs_jasser.jsonahmose_seo_yoon_vs_jasser0——0.00%—added
testcases/emulator_verified/ahmose_bucket_interactions/ahmose_vs_gatot.jsonahmose_vs_gatot0——0.00%—added
testcases/emulator_verified/ahmose_bucket_interactions/ahmose_vs_wu_ming.jsonahmose_vs_wu_ming0——0.00%—added
testcases/emulator_verified/ahmose_bucket_interactions/wu_ming_vs_ahmose.jsonwu_ming_vs_ahmose0——0.00%—added
testcases/emulator_verified/ahmose_c2_discriminators/ahmose_renee_vs_jasser_01.jsonahmose_renee_vs_jasser_010——0.00%—added
testcases/emulator_verified/ahmose_c2_discriminators/ahmose_renee_vs_jasser_02.jsonahmose_renee_vs_jasser_020——0.00%—added
testcases/emulator_verified/ahmose_c2_discriminators/ahmose_vs_jasser_02.jsonahmose_vs_jasser_020——0.00%—added
testcases/emulator_verified/ahmose_c2_discriminators/ahmose_vs_jasser_03.jsonahmose_vs_jasser_030——0.00%—added
testcases/emulator_verified/ahmose_c2_discriminators/ahmose_vs_sonya_01.jsonahmose_vs_sonya_010——0.00%—added
testcases/emulator_verified/ahmose_c2_discriminators/ahmose_vs_sonya_02.jsonahmose_vs_sonya_020——0.00%—added
testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_02.jsonjasser_vs_ahmose_020——0.00%—added
testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_03.jsonjasser_vs_ahmose_030——0.00%—added
testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_04.jsonjasser_vs_ahmose_040——0.00%—added
testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_05.jsonjasser_vs_ahmose_050——0.00%—added
testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_renee_01.jsonjasser_vs_ahmose_renee_010——0.00%—added
testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_renee_02.jsonjasser_vs_ahmose_renee_020——0.00%—added
testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_01.jsonsonya_vs_ahmose_010——0.00%—added
testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_02.jsonsonya_vs_ahmose_020——0.00%—added
testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_03.jsonsonya_vs_ahmose_030——0.00%—added
testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_04.jsonsonya_vs_ahmose_040——0.00%—added
testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_05.jsonsonya_vs_ahmose_050——0.00%—added
testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_06.jsonsonya_vs_ahmose_060——0.00%—added
testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_07.jsonsonya_vs_ahmose_070——0.00%—added
testcases/emulator_verified/ahmose_gordon_renee/ahmose_bradley_vs_wuming.jsonahmose_bradley_vs_wuming0——0.00%—added
testcases/emulator_verified/ahmose_gordon_renee/ahmose_gordon_vs_jasser_control.jsonahmose_gordon_vs_jasser_control0——0.00%—added
testcases/emulator_verified/ahmose_gordon_renee/ahmose_gordon_vs_wuming.jsonahmose_gordon_vs_wuming0-0.005%—1.63%—added
testcases/emulator_verified/ahmose_gordon_renee/ahmose_gordon_wayne_pure_lancers_vs_jasser.jsonahmose_gordon_wayne_pure_lancers_vs_jasser0-0.005%—0.00%—added
testcases/emulator_verified/ahmose_gordon_renee/ahmose_gordon_wayne_vs_jasser.jsonahmose_gordon_wayne_vs_jasser0+0.005%—-0.38%—added
testcases/emulator_verified/ahmose_gordon_renee/ahmose_renee_vs_jasser_addition.jsonahmose_renee_vs_jasser_addition0——0.00%—added
testcases/emulator_verified/ahmose_gordon_renee/ahmose_renee_vs_jasser_control.jsonahmose_renee_vs_jasser_control0——0.00%—added
testcases/emulator_verified/ahmose_gordon_renee/ahmose_renee_vs_jasser_mark_kind.jsonahmose_renee_vs_jasser_mark_kind0——0.00%—added
testcases/emulator_verified/ahmose_gordon_renee/ahmose_wayne_vs_jasser.jsonahmose_wayne_vs_jasser0+0.005%—-2.34%—added
testcases/emulator_verified/ahmose_gordon_renee/ahmose_wayne_vs_wuming.jsonahmose_wayne_vs_wuming0+0.005%—-2.10%—added
testcases/emulator_verified/ahmose_gordon_renee/wuming_renee_vs_jasser.jsonwuming_renee_vs_jasser0——0.00%—added
testcases/emulator_verified/ahmose_isolation_20260925/ahmose_1000i_vs_wuming_1000i.jsonahmose_1000i_vs_wuming_1000i0——0.00%—added
testcases/emulator_verified/ahmose_isolation_20260925/ahmose_bradley_1000i_vs_3700i.jsonahmose_bradley_1000i_vs_3700i0——0.00%—added
testcases/emulator_verified/gatot_sonya_hendrik_precise.jsonc2_27346931709781040——0.00%—added
testcases/emulator_verified/gordon_interactions_20260925/gordon_wayne_1200l_vs_4800l.jsongordon_wayne_1200l_vs_4800l0——0.00%—added
testcases/emulator_verified/gordon_interactions_20260925/gordon_wayne_800l_vs_3200l.jsongordon_wayne_800l_vs_3200l0-0.005%—0.00%—added
testcases/emulator_verified/gordon_interactions_20260925/wayne_control_1000l_vs_1700l.jsonwayne_control_1000l_vs_1700l0——0.00%—added
testcases/emulator_verified/gordon_isolation_20260925/gordon_solo_1329l_vs_5000l.jsongordon_solo_1329l_vs_5000l0-0.003%—0.00%—added
testcases/emulator_verified/gordon_isolation_20260925/gordon_solo_180l_vs_700l.jsongordon_solo_180l_vs_700l0——0.00%—added
testcases/emulator_verified/gordon_isolation_20260925/gordon_solo_1860l_vs_7000l.jsongordon_solo_1860l_vs_7000l0+0.004%—0.00%—added
testcases/emulator_verified/gordon_isolation_20260925/nohero_300l_vs_600l.jsonnohero_300l_vs_600l0——0.00%—added
testcases/emulator_verified/gwen_blastmaster_replay_550m_vs_810i_45l_45m.jsongwen_blastmaster_replay_550m_vs_810i_45l_45m0——0.00%—added
testcases/emulator_verified/gwen_c2_twenty/ahmose_gordon_gwen_vs_gatot_patrick_hendrik.jsonahmose_gordon_gwen_vs_gatot_patrick_hendrik0+0.005%—-0.05%—added
testcases/emulator_verified/gwen_c2_twenty/ahmose_renee_gwen_vs_sergey_patrick_hendrik.jsonahmose_renee_gwen_vs_sergey_patrick_hendrik0——0.00%—added
testcases/emulator_verified/gwen_c2_twenty/edith_patrick_gwen_vs_ahmose_renee_bradley.jsonedith_patrick_gwen_vs_ahmose_renee_bradley0——0.00%—added
testcases/emulator_verified/gwen_c2_twenty/flint_patrick_gwen_vs_ahmose_renee_jasser.jsonflint_patrick_gwen_vs_ahmose_renee_jasser0——0.00%—added
testcases/emulator_verified/gwen_c2_twenty/gatot_renee_gwen_vs_sergey_sonya_wayne.jsongatot_renee_gwen_vs_sergey_sonya_wayne0——0.00%—added
testcases/emulator_verified/gwen_c2_twenty/gatot_sonya_gwen_vs_edith_gordon_bradley.jsongatot_sonya_gwen_vs_edith_gordon_bradley0-0.005%—0.05%—added
testcases/emulator_verified/gwen_c2_twenty/gwen_vs_ahmose.jsongwen_vs_ahmose0——0.00%—added
testcases/emulator_verified/gwen_c2_twenty/gwen_vs_bradley.jsongwen_vs_bradley0——0.00%—added
testcases/emulator_verified/gwen_c2_twenty/gwen_vs_flint_jessie_wayne.jsongwen_vs_flint_jessie_wayne0——0.00%—added
testcases/emulator_verified/gwen_c2_twenty/gwen_vs_gatot.jsongwen_vs_gatot0——0.00%—added
testcases/emulator_verified/gwen_c2_twenty/gwen_vs_hendrik.jsongwen_vs_hendrik0——0.00%—added
testcases/emulator_verified/gwen_c2_twenty/gwen_vs_patrick.jsongwen_vs_patrick0——0.00%—added
testcases/emulator_verified/gwen_c2_twenty/gwen_vs_renee.jsongwen_vs_renee0——0.00%—added
testcases/emulator_verified/gwen_c2_twenty/gwen_vs_sergey.jsongwen_vs_sergey0——0.00%—added
testcases/emulator_verified/gwen_c2_twenty/gwen_vs_sonya.jsongwen_vs_sonya0——0.00%—added
testcases/emulator_verified/gwen_c2_twenty/gwen_vs_wu_ming_sonya_jasser.jsongwen_vs_wu_ming_sonya_jasser0——0.00%—added
testcases/emulator_verified/gwen_c2_twenty/sergey_jessie_hendrik_vs_gwen.jsonsergey_jessie_hendrik_vs_gwen0——0.00%—added
testcases/emulator_verified/gwen_c2_twenty/wu_ming_gordon_gwen_vs_gatot_jessie_hendrik.jsonwu_ming_gordon_gwen_vs_gatot_jessie_hendrik0-0.005%—0.06%—added
testcases/emulator_verified/gwen_c2_twenty/wu_ming_sonya_bradley_vs_gatot_patrick_gwen.jsonwu_ming_sonya_bradley_vs_gatot_patrick_gwen0——0.00%—added
testcases/emulator_verified/gwen_c2_twenty/wu_ming_sonya_gwen_vs_edith_gordon_bradley.jsonwu_ming_sonya_gwen_vs_edith_gordon_bradley0——0.00%—added
testcases/emulator_verified/gwen_gatot_cadence/gwen_60m_vs_gatot_400i.jsongwen_60m_vs_gatot_400i0——0.00%—added
testcases/emulator_verified/gwen_gatot_cadence/gwen_65m_vs_gatot_200i.jsongwen_65m_vs_gatot_200i0——0.00%—added
testcases/emulator_verified/gwen_s1_probes/gwen_s1_a.jsongwen_s1_a0——0.00%—added
testcases/emulator_verified/gwen_s1_probes/gwen_s1_b.jsongwen_s1_b0——0.00%—added
testcases/emulator_verified/gwen_s1_probes/gwen_s1_c.jsongwen_s1_c0——0.00%—added
testcases/emulator_verified/gwen_s1_probes/gwen_s1_d.jsongwen_s1_d0——0.00%—added
testcases/emulator_verified/gwen_s1_probes/gwen_s1_e.jsongwen_s1_e0——0.00%—added
testcases/emulator_verified/gwen_s2_probes/gwen_s2_calibration.jsongwen_s2_calibration0——0.00%—added
testcases/emulator_verified/gwen_s2_probes/gwen_s2_curse_timing.jsongwen_s2_curse_timing0——0.00%—added
testcases/emulator_verified/gwen_s2_probes/gwen_s2_first_trigger.jsongwen_s2_first_trigger0——0.00%—added
testcases/emulator_verified/gwen_s2_probes/gwen_s2_mark_consumption.jsongwen_s2_mark_consumption0——0.00%—added
testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_1.jsongwen_s2_source_10——0.00%—added
testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_2.jsongwen_s2_source_20——0.00%—added
testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_3.jsongwen_s2_source_30——0.00%—added
testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_4.jsongwen_s2_source_40——0.00%—added
testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_5.jsongwen_s2_source_50——0.00%—added
testcases/emulator_verified/interaction_map/gordon_bradley_pure_lancers.jsongordon_bradley_pure_lancers0——0.00%—added
testcases/emulator_verified/interaction_map/gordon_wuming_pure_lancers.jsongordon_wuming_pure_lancers0-0.005%—0.03%—added
testcases/emulator_verified/interaction_map/renee_bradley_pure_lancers.jsonrenee_bradley_pure_lancers0——0.00%—added
testcases/emulator_verified/interaction_map/renee_jasser_pure_lancers.jsonrenee_jasser_pure_lancers0——0.00%—added
testcases/emulator_verified/interaction_map/renee_wuming_pure_lancers.jsonrenee_wuming_pure_lancers0——0.00%—added
testcases/emulator_verified/nohero_controls/nohero_explicit_empty_100m_vs_100m.jsonnohero_explicit_empty_100m_vs_100m0——0.00%—added
testcases/emulator_verified/renee_isolation_20260925/renee_gwen_300l_100m_vs_2700l.jsonrenee_gwen_300l_100m_vs_2700l0-0.005%—0.97%—added
testcases/emulator_verified/renee_isolation_20260925/renee_mark_source_death.jsonrenee_mark_source_death0——0.00%—added
testcases/emulator_verified/renee_s2_bucket_confirmations/gatot_shield_500_150.jsongatot_shield_500_1500——0.00%—added
testcases/emulator_verified/renee_s2_bucket_confirmations/gwen_next_hit_150_1050.jsongwen_next_hit_150_10500-0.005%—0.92%—added
testcases/emulator_verified/renee_s2_families/defense_gatot.jsondefense_gatot0——0.00%—added
testcases/emulator_verified/renee_s2_families/defense_hendrik.jsondefense_hendrik0——0.00%—added
testcases/emulator_verified/renee_s2_families/defense_logan.jsondefense_logan0——0.00%—added
testcases/emulator_verified/renee_s2_families/defense_lumak.jsondefense_lumak0——0.00%—added
testcases/emulator_verified/renee_s2_families/defense_patrick.jsondefense_patrick0——0.00%—added
testcases/emulator_verified/renee_s2_families/defense_sergey.jsondefense_sergey0——0.00%—added
testcases/emulator_verified/renee_s2_families/defense_wuming.jsondefense_wuming0——0.00%—added
testcases/emulator_verified/renee_s2_families/renee_edith_hendrik.jsonrenee_edith_hendrik0——0.00%—added
testcases/emulator_verified/renee_s2_families/renee_gwen.jsonrenee_gwen0-0.005%—2.54%—added
testcases/emulator_verified/renee_s2_families/renee_gwen_25_marksmen.jsonrenee_gwen_25_marksmen0-0.005%—1.12%—added
testcases/emulator_verified/renee_s2_families/renee_gwen_no_marksmen.jsonrenee_gwen_no_marksmen0-0.005%—0.45%—added
testcases/emulator_verified/renee_s2_families/renee_seo_charge.jsonrenee_seo_charge0——0.00%—added
testcases/emulator_verified/renee_s2_families/skill_fleet_captain.jsonskill_fleet_captain0——-0.66%—added

Code / Config Changes

Commits (7f6ec1ee → 2b3f7928)

2b3f7928Various issues causng small sim-game diferences fixedpiddlyminx25/09/2026, 05:51:17

Code Changes (Run A → Run B)

Index: simulator/config/hero_definitions/Ahmose.json
===================================================================
--- simulator/config/hero_definitions/Ahmose.json prev run
+++ simulator/config/hero_definitions/Ahmose.json this run
@@ -5,10 +5,11 @@
"skills": {
"ViperFormation": {
"description": "His infantry pauses the attack once every 4 times reducing damage taken by Lancers and Marksmen by X% and Infantry by X% for 2 turns",
"trigger": {
- "type": "turn",
- "every": 4
+ "type": "attack",
+ "every": 4,
+ "source": "infantry"
},
"effects": {
"ViperFormation/1": {
"type": "no_attack",
@@ -18,57 +19,56 @@
]
},
"duration": {
"turns": {
- "count": 1
+ "count": 1,
+ "delay": 1
}
},
- "reason": "Protection is earned by using the Infantry pause. Delay its child effects so protection covers two complete turns, independent of attack order.",
- "trigger_effects": {
- "ViperFormation/2": {
- "type": "active.hero.damageTaken.down",
- "value": [
- 10,
- 15,
- 20,
- 25,
- 30
- ],
- "units": {
- "applies_to": [
- "lancer",
- "marksman"
- ]
- },
- "duration": {
- "turns": {
- "count": 2,
- "delay": 1
- }
- }
- },
- "ViperFormation/3": {
- "type": "active.hero.damageTaken.down",
- "value": [
- 10,
- 25,
- 40,
- 55,
- 70
- ],
- "units": {
- "applies_to": [
- "infantry"
- ]
- },
- "duration": {
- "turns": {
- "count": 2,
- "delay": 1
- }
- }
+ "reason": "Protection starts when the Infantry pause is used, with no additional turn delay."
+ },
+ "ViperFormation/2": {
+ "type": "active.hero.damageTaken.down",
+ "value": [
+ 10,
+ 15,
+ 20,
+ 25,
+ 30
+ ],
+ "units": {
+ "applies_to": [
+ "lancer",
+ "marksman"
+ ]
+ },
+ "duration": {
+ "turns": {
+ "count": 2,
+ "delay": 1
}
}
+ },
+ "ViperFormation/3": {
+ "type": "active.hero.damageTaken.down",
+ "value": [
+ 10,
+ 25,
+ 40,
+ 55,
+ 70
+ ],
+ "units": {
+ "applies_to": [
+ "infantry"
+ ]
+ },
+ "duration": {
+ "turns": {
+ "count": 2,
+ "delay": 1
+ }
+ }
}
}
},
"PrayerOfFlame": {
@@ -101,9 +101,9 @@
"source": "infantry"
},
"effects": {
"BladeOfLight/1": {
- "type": "active.hero.damage.up",
+ "type": "extra_skill_attack",
"value": [
12,
24,
36,
@@ -114,13 +114,14 @@
"applies_to": [
"infantry"
]
},
- "duration": {
- "attacks": {
- "count": 1
+ "trigger_damage_jobs": [
+ {
+ "source": "use.source",
+ "target": "use.target"
}
- }
+ ]
},
"BladeOfLight/2": {
"type": "active.hero.damageTaken.up",
"value": [
Index: simulator/config/hero_definitions/Gordon.json
===================================================================
--- simulator/config/hero_definitions/Gordon.json prev run
+++ simulator/config/hero_definitions/Gordon.json this run
@@ -21,10 +21,10 @@
80,
100
],
"units": {
- "applies_to": "trigger.source",
- "applies_vs": "trigger.target"
+ "applies_to": "trigger.source",
+ "applies_vs": "trigger.target"
},
"duration": {
"attacks": {
"count": 1
@@ -60,8 +60,9 @@
},
"effects": {
"ChemicalTerror/1": {
"type": "active.hero.damage.up",
+ "applies_to_damage_kinds": ["normal"],
"value": [
30,
60,
90,
@@ -74,9 +75,10 @@
]
},
"duration": {
"turns": {
- "count": 1
+ "count": 1,
+ "delay": 1
}
},
"same_effect_stacking": "max"
},
@@ -98,8 +100,30 @@
"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/Gwen.json
===================================================================
--- simulator/config/hero_definitions/Gwen.json prev run
+++ simulator/config/hero_definitions/Gwen.json this run
@@ -19,9 +19,9 @@
20,
25
],
"units": {
- "applies_to": "enemy.any"
+ "applies_to": "trigger.target"
},
"duration": {
"attacks": {
"count": 1,
@@ -34,9 +34,8 @@
"AirDominance": {
"description": "Grants all troops' attack X% extra damage after every 5 attacks and causes the target to receive X% extra damage for its next attack received",
"trigger": {
"type": "attack",
- "first": 5,
"every": 6,
"source": "self.any"
},
"effects": {
@@ -57,30 +56,28 @@
{
"source": "use.source",
"target": "use.target"
}
- ]
- },
- "AirDominance/2": {
- "type": "active.hero.damageTaken.up",
- "value": [
- 5,
- 7.5,
- 10,
- 12.5,
- 15
],
- "units": {
- "applies_to": "trigger.target"
- },
- "same_effect_stacking": "max",
- "duration": {
- "turns": {
- "count": 1,
- "delay": 1
- },
- "attacks": {
- "count": 1
+ "trigger_effects": {
+ "AirDominance/2": {
+ "type": "active.hero.damageTaken.up",
+ "value": [
+ 5,
+ 7.5,
+ 10,
+ 12.5,
+ 15
+ ],
+ "units": {
+ "applies_to": "parent.use.target"
+ },
+ "same_effect_stacking": "max",
+ "duration": {
+ "attacks": {
+ "count": 1
+ }
+ }
}
}
}
}
@@ -88,10 +85,9 @@
"Blastmaster": {
"description": "Gwen equips her Marksmen with grenades, dealing X% extra damage to all enemies on the next attack of every 4 attacks",
"trigger": {
"type": "attack",
- "first": 5,
- "every": 4,
+ "every": 5,
"source": "marksman"
},
"effects": {
"Blastmaster/1": {
Index: simulator/config/hero_definitions/README.md
===================================================================
--- simulator/config/hero_definitions/README.md prev run
+++ simulator/config/hero_definitions/README.md this run
@@ -347,8 +347,10 @@
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.
+
**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`
@@ -452,12 +454,16 @@
If selectors produce multiple sources and targets, the simulator considers their Cartesian product. Sources or targets with zero round-start troops are skipped. In normal PvP mode, a target already exhausted by earlier same-round jobs is also skipped before calculation. For example, one source and `enemy.living` can produce up to three skill jobs, but fewer may actually run.
The job selector determines the actual side as well as the unit. The runtime does not enforce that a generated source and target are opponents, so scopes misconfigured onto the same side can generate friendly-fire or same-side jobs.
+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.
-When an `extra_skill_attack` is materialized as a carrier's `trigger_effects` child, its completed damage result is captured in the carrier-use round and stored on that child ActiveEffect. The child can deliver that fixed result only when a later normal attack matches its resolved source and target scope. Delivery does not run the damage equation again; if the matching attack never occurs during the child's duration, the pending damage expires with the effect.
+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.
+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.
+
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.
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.
Index: simulator/config/hero_definitions/Renee.json
===================================================================
--- simulator/config/hero_definitions/Renee.json prev run
+++ simulator/config/hero_definitions/Renee.json this run
@@ -23,9 +23,9 @@
"NightmareTrace/1": {
"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": [
{
@@ -95,9 +95,8 @@
},
"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",
"applies_vs": "any"
Index: simulator/config/troop_skills.json
===================================================================
--- simulator/config/troop_skills.json prev run
+++ simulator/config/troop_skills.json this run
@@ -11,23 +11,23 @@
"value": 0
}
],
"trigger": {
- "type": "battle_start"
+ "type": "attack",
+ "source": "infantry",
+ "target": "lancer"
},
"effects": {
"MasterBrawler/1": {
"type": "type.single_target.damage.up",
"value": [
10
],
"units": {
- "applies_to": [
- "infantry"
- ],
- "applies_vs": [
- "lancer"
- ]
+ "applies_to": "trigger.source"
+ },
+ "duration": {
+ "turns": { "count": 1 }
}
}
}
},
@@ -162,23 +162,23 @@
"value": 0
}
],
"trigger": {
- "type": "battle_start"
+ "type": "attack",
+ "source": "lancer",
+ "target": "marksman"
},
"effects": {
"Charge/1": {
"type": "type.single_target.damage.up",
"value": [
10
],
"units": {
- "applies_to": [
- "lancer"
- ],
- "applies_vs": [
- "marksman"
- ]
+ "applies_to": "trigger.source"
+ },
+ "duration": {
+ "turns": { "count": 1 }
}
}
}
},
@@ -320,23 +320,23 @@
"value": 0
}
],
"trigger": {
- "type": "battle_start"
+ "type": "attack",
+ "source": "marksman",
+ "target": "infantry"
},
"effects": {
"RangedStrike/1": {
"type": "type.single_target.damage.up",
"value": [
10
],
"units": {
- "applies_to": [
- "marksman"
- ],
- "applies_vs": [
- "infantry"
- ]
+ "applies_to": "trigger.source"
+ },
+ "duration": {
+ "turns": { "count": 1 }
}
}
}
},
Index: simulator/src/damage.test.ts
===================================================================
--- simulator/src/damage.test.ts prev run
+++ simulator/src/damage.test.ts this run
@@ -1,19 +1,22 @@
import assert from "node:assert/strict";
import { test } from "node:test";
import {
- calculateDamageJob,
+ generateDamageJob,
+ deliverDamageJob,
+ retainGeneratedDamage,
+ type DamageJobOptions,
ceilIgnoringFloatResidue,
createDamageScratch,
evaluateDamageExpression
} from "./damage";
import { DYNAMIC_BUCKETS, STATIC_BUCKETS, type BucketJobSide, type BucketPlacement } from "./damageBuckets";
-import { createEffectIndex, damageShapeSlotsForEffect, DAMAGE_JOB_SHAPE_SLOTS, indexEffect } from "./effectIndex";
+import { createEffectIndex, damageShapeSlotsForEffect, DAMAGE_JOB_SHAPE_SLOTS, expireEffectIndex, indexEffect } from "./effectIndex";
import { activateEffect, evolvingActiveEffectValue, resolvedEffectScopeKey } from "./effects";
import { buildStaticDamageBucketFactors, buildStaticDamageProfile } from "./staticDamageProfile";
-import { createRecorder, type BattleRecorder } from "./recorder";
-import type { ActiveEffect, DamageJob, ResolvedFighter } from "./types";
+import { createRecorder } from "./recorder";
+import type { ActiveEffect, DamageJob, EvolvingActiveEffect, ResolvedFighter } from "./types";
import { ALL_UNIT_MASK, unitMask } from "./types";
const job: DamageJob = {
round: 1,
@@ -61,10 +64,21 @@
function calculateIndexedDamageJob(
damageJob: DamageJob,
fighters: Record<"attacker" | "defender", ResolvedFighter>,
effects: ActiveEffect[],
- options: Omit<Partial<Parameters<typeof calculateDamageJob>[2]>, "recorder"> & { recorder?: BattleRecorder; trace?: boolean } = {}
+ options: Partial<DamageJobOptions> & { trace?: boolean } = {}
) {
+ const preparedOptions = damageJobOptions(fighters, effects, options);
+ const generated = generateDamageJob(damageJob, fighters, preparedOptions);
+ const result = deliverDamageJob(damageJob, generated, preparedOptions);
+ return { ...result, usedEffectIds: preparedOptions.usedEffects!.map((usedEffect) => usedEffect.intent.id) };
+}
+
+function damageJobOptions(
+ fighters: Record<"attacker" | "defender", ResolvedFighter>,
+ effects: ActiveEffect[],
+ options: Partial<DamageJobOptions> & { trace?: boolean } = {}
+): DamageJobOptions {
const effectIndex = options.effectIndex ?? preparedEffectIndex(effects);
if (!options.effectIndex) {
for (const activeEffect of effects) {
if (activeEffect.effectGroup) indexEffect(effectIndex, activeEffect);
@@ -76,10 +90,9 @@
throw new Error("damage-only test recorder has no battle resolution");
});
recorder.recordStaticProfile(fighters, effects);
const { trace: _trace, ...damageOptions } = options;
- const result = calculateDamageJob(damageJob, fighters, { ...damageOptions, recorder, effectIndex, staticDamageProfile, usedEffects });
- return { ...result, usedEffectIds: [...usedEffects].map((usedEffect) => usedEffect.intent.id) };
+ return { ...damageOptions, recorder, effectIndex, staticDamageProfile, usedEffects };
}
function preparedEffectIndex(effects: ActiveEffect[]): ReturnType<typeof createEffectIndex> {
const groups: NonNullable<ActiveEffect["effectGroup"]>[] = [];
@@ -97,17 +110,19 @@
sameEffectStacking: activeEffect.sameEffectStacking
};
byResolvedGroup.set(key, group);
groups.push(group);
- for (const slot of slots) byShape[slot].push(group);
+ if (activeEffect.kind !== "shield") {
+ for (const slot of slots) byShape[slot].push(group);
+ }
}
activeEffect.effectGroup = group;
}
return createEffectIndex(groups, byShape);
}
test("damage calculator requires indexed effect candidates", () => {
- assert.throws(() => calculateDamageJob(job, simpleFighters(), { trace: true } as never), /effectIndex/i);
+ assert.throws(() => generateDamageJob(job, simpleFighters(), { trace: true } as never), /effectIndex/i);
});
test("damage calculator counts every positive fractional remainder as one living troop", () => {
const fractionalJob: DamageJob = {
@@ -472,8 +487,171 @@
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 = {
+ ...effect("active.hero.shield", "defender", baseline * 0.5),
+ kind: "shield",
+ duration: { turns: { count: 2 } }
+ };
+ const second: ActiveEffect = {
+ ...effect("active.hero.shield", "defender", baseline * 0.5),
+ kind: "shield",
+ duration: { turns: { count: 2 } }
+ };
+ const troop: ActiveEffect = {
+ ...effect("active.troop.shield", "defender", baseline * 0.5, "troop_skill"),
+ kind: "shield",
+ duration: { turns: { count: 2 } }
+ };
+ const options = damageJobOptions(fighters, [first, second, troop], { trace: true });
+ const outcomes = Array.from({ length: 3 }, () => deliverDamageJob(job, generateDamageJob(job, fighters, options), options));
+ assert.equal(outcomes[0].kills, 0);
+ assert.ok(Math.abs(outcomes[1].kills - baseline * 0.5) < 1e-12);
+ assert.equal(outcomes[2].kills, baseline);
+ assert.ok(Math.abs(outcomes.reduce((total, outcome) => total + outcome.trace!.offsetDamage, 0) - baseline * 1.5) < 1e-12);
+});
+
+test("max-stacked turn shields drain suppressed siblings without adding their protection", () => {
+ const fighters = simpleFighters();
+ const baseline = calculateIndexedDamageJob(job, fighters, []).kills;
+ const weaker: ActiveEffect = {
+ ...effect("active.hero.shield", "defender", baseline * 1.25),
+ intent: { id: "weaker", type: "active.hero.shield", value: baseline * 1.25 },
+ kind: "shield",
+ duration: { turns: { count: 2 } },
+ sameEffectStacking: "max"
+ };
+ const stronger: ActiveEffect = {
+ ...effect("active.hero.shield", "defender", baseline * 1.5),
+ intent: { id: "stronger", type: "active.hero.shield", value: baseline * 1.5 },
+ kind: "shield",
+ duration: { turns: { count: 2 } },
+ sameEffectStacking: "max"
+ };
+ const options = damageJobOptions(fighters, [weaker, stronger], { trace: true });
+ const first = deliverDamageJob(job, generateDamageJob(job, fighters, options), options);
+ assert.equal(first.kills, 0);
+ assert.deepEqual(options.usedEffects!.map((active) => active.intent.id).sort(), ["stronger", "weaker"]);
+ assert.deepEqual(first.appliedEffects?.filter((applied) => "kind" in applied && applied.kind === "shield").map((applied) => "value" in applied ? applied.value : undefined), [baseline]);
+ const second = deliverDamageJob(job, generateDamageJob(job, fighters, options), options);
+ const third = deliverDamageJob(job, generateDamageJob(job, fighters, options), options);
+ assert.ok(Math.abs(second.kills - baseline * 0.5) < 1e-12);
+ assert.equal(third.kills, baseline);
+});
+
+test("only shield-eligible damage kinds and source-target scopes advance attack delay", () => {
+ const fighters = simpleFighters();
+ const shield: ActiveEffect = {
+ ...effect("active.hero.shield", "defender", 4),
+ kind: "shield",
+ intent: { id: "scoped-shield", type: "active.hero.shield", value: 4, applies_to_damage_kinds: ["normal"] },
+ appliesTo: { side: "defender", units: unitMask("lancer") },
+ appliesVs: { side: "attacker", units: unitMask("infantry") },
+ remainingAttackDelay: 1
+ };
+ const options = damageJobOptions(fighters, [shield], { trace: true });
+ const reverse: DamageJob = {
+ ...job, dealerSide: "defender", dealerUnit: "lancer", takerSide: "attacker", takerUnit: "infantry"
+ };
+ for (const ineligible of [{ ...job, kind: "skill" as const }, reverse]) {
+ const outcome = deliverDamageJob(ineligible, generateDamageJob(ineligible, fighters, options), options);
+ assert.equal(outcome.kills, calculateIndexedDamageJob(ineligible, fighters, []).kills);
+ assert.equal(outcome.trace?.offsetDamage, 0);
+ }
+ const firstEligible = deliverDamageJob(job, generateDamageJob(job, fighters, options), options);
+ const secondEligible = deliverDamageJob(job, generateDamageJob(job, fighters, options), options);
+ assert.equal(firstEligible.trace?.offsetDamage, 0);
+ assert.equal(secondEligible.trace?.offsetDamage, 4);
+ assert.equal(secondEligible.kills, Math.max(0, firstEligible.kills - 4));
+});
+
test("a durationless shield subtracts its full raw value from normal and skill damage jobs", () => {
const fighters = simpleFighters();
const shield = {
...effect("active.hero.shield", "defender", 25),
@@ -550,8 +728,58 @@
: 0;
assert.equal(infantryApplied + lancerApplied, Math.min(shieldValue, infantryBaseline.kills + lancerBaseline.kills));
});
+test("source-attack shields conserve their budget when health modifiers change", () => {
+ const fighters = simpleFighters();
+ const incoming = { ...job, sourceMultiplier: 0.3 };
+ const health = effect("active.hero.health.up", "defender", 100);
+ const shield: ActiveEffect = {
+ ...effect("active.hero.shield", "defender", 4),
+ kind: "shield",
+ duration: { turns: { count: 1 } },
+ intent: {
+ id: "source-shield",
+ type: "active.hero.shield",
+ value: 4,
+ value_formula: { type: "percent_of", source: "trigger.source_attack" }
+ }
+ };
+
+ const first = calculateIndexedDamageJob(incoming, fighters, [health, shield]);
+ const afterHealthExpires = calculateIndexedDamageJob(incoming, fighters, [shield]);
+ const afterShieldExhaustion = calculateIndexedDamageJob(incoming, fighters, [health, shield]);
+
+ assert.equal(first.kills, 0);
+ assert.ok(Math.abs(afterHealthExpires.kills - 2) < 1e-12);
+ assert.ok(Math.abs(afterShieldExhaustion.kills - 1.5) < 1e-12);
+});
+
+test("kill-derived shield budgets are not converted twice by health modifiers", () => {
+ const fighters = simpleFighters();
+ const incoming = { ...job, sourceMultiplier: 0.3 };
+ const health = effect("active.hero.health.up", "defender", 100);
+ const shield: ActiveEffect = {
+ ...effect("active.hero.shield", "defender", 4),
+ kind: "shield",
+ duration: { turns: { count: 1 } },
+ intent: {
+ id: "kill-shield",
+ type: "active.hero.shield",
+ value: 4,
+ value_formula: { type: "percent_of", source: "trigger.total_kills" }
+ }
+ };
+
+ const first = calculateIndexedDamageJob(incoming, fighters, [health, shield]);
+ const second = calculateIndexedDamageJob(incoming, fighters, [health, shield]);
+ const third = calculateIndexedDamageJob(incoming, fighters, [health, shield]);
+
+ assert.equal(first.kills, 0);
+ assert.equal(second.kills, 0);
+ assert.ok(Math.abs(third.kills - 0.5) < 1e-12);
+});
+
test("an attack-duration shield keeps its full value in a mixed-formation army", () => {
const fighters = simpleFighters();
fighters.attacker.initialTroops = { infantry: 1000, lancer: 10, marksman: 0 };
fighters.attacker.troopDetails.lancer = {
Index: simulator/src/damage.ts
===================================================================
--- simulator/src/damage.ts prev run
+++ simulator/src/damage.ts this run
@@ -25,9 +25,9 @@
type DynamicDamageBucket,
type StaticDamageBucket
} from "./damageBuckets";
import { advanceEffectAttackDelay, isEffectAttackReady } from "./effects";
-import { damageJobShapeSlot, damageJobSlot, type EffectIndex } from "./effectIndex";
+import { damageJobSlot, shieldEffectApplies, type EffectIndex } from "./effectIndex";
import type { BattleRecorder, DamageJobRecorder } from "./recorder";
type NumericBucketId = number;
interface DamageFactorTerm {
@@ -39,8 +39,9 @@
interface NumericDamageBuckets {
factors: Float64Array;
turnShield?: ActiveEffect;
turnShieldGroup?: ActiveEffect[];
+ generated?: GeneratedDamage;
}
interface CompiledDamageExpression {
numeratorSlots: Int32Array;
@@ -113,32 +114,91 @@
primaryUsedEffects?: ActiveEffect[];
minInitialArmy?: number;
}
-export function calculateDamageJob(
+/** Borrowed during immediate delivery; retainGeneratedDamage owns delayed state. */
+export interface GeneratedDamage {
+ factors?: Float64Array;
+ recording: DamageJobRecorder;
+ armyTerm: number;
+ damageBeforeOffsets: number;
+ healthFactor: number;
+}
+
+export function generateDamageJob(
job: DamageJob,
fighters: Record<SideId, ResolvedFighter>,
options: DamageJobOptions
-): DamageResult {
- if (!options?.effectIndex) throw new Error("calculateDamageJob requires an effectIndex");
- if (!options.recorder) throw new Error("calculateDamageJob requires a recorder");
- if (!options.staticDamageProfile) throw new Error("calculateDamageJob requires a staticDamageProfile");
+): GeneratedDamage {
+ if (!options?.effectIndex) throw new Error("generateDamageJob requires an effectIndex");
+ if (!options.recorder) throw new Error("generateDamageJob requires a recorder");
+ if (!options.staticDamageProfile) throw new Error("generateDamageJob requires a staticDamageProfile");
const recording = options.recorder.startDamageJob();
- const staticProfile = options.staticDamageProfile;
+ const buckets = options.scratch ? resetDamageScratch(options.scratch) : createNumericDamageBuckets();
+ const usedEffects = options.usedEffects ?? [];
+ const armyTerm = populateDamageBuckets(job, fighters, options, buckets, recording, usedEffects, options.primaryUsedEffects ?? usedEffects);
+ const generated = buckets.generated ??= {
+ factors: buckets.factors, recording, armyTerm, damageBeforeOffsets: 0, healthFactor: 1
+ };
+ generated.recording = recording;
+ generated.armyTerm = armyTerm;
+ generated.damageBeforeOffsets = evaluateDamageExpressionForJob(job, buckets, options.staticDamageProfile);
+ generated.healthFactor = buckets.factors[DYNAMIC_BUCKET_INDEX["active.hero.health.up"]] *
+ buckets.factors[DYNAMIC_BUCKET_INDEX["active.troop.health.up"]] /
+ buckets.factors[DYNAMIC_BUCKET_INDEX["active.hero.health.down"]] /
+ 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
+ };
+}
+
+export function deliverDamageJob(
+ job: DamageJob,
+ generated: GeneratedDamage,
+ options: DamageJobOptions
+): DamageResult {
+ 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);
+ }
+ const usedEffects = options.usedEffects ?? [];
+ const primaryUsedEffects = options.primaryUsedEffects ?? usedEffects;
+ const offsetDamage = 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 });
+}
+
+function populateDamageBuckets(
+ job: DamageJob,
+ fighters: Record<SideId, ResolvedFighter>,
+ options: DamageJobOptions,
+ buckets: NumericDamageBuckets,
+ recording: DamageJobRecorder,
+ 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 takerTroops = job.roundStartTroops[job.takerSide][job.takerUnit] ?? 0;
const initialArmy = options.minInitialArmy ?? minInitialArmy(fighters);
const armyTerm = Math.sqrt(dealerTroops) * Math.sqrt(initialArmy);
- const buckets = options.scratch ? resetDamageScratch(options.scratch) : createNumericDamageBuckets();
applyDynamicDamageBucketValue(buckets, "troops.count", armyTerm);
applyDynamicDamageBucketValue(buckets, "source.multiplier", job.sourceMultiplier ?? 1);
- const usedEffects = options.usedEffects ?? [];
- const primaryUsedEffects = options.primaryUsedEffects ?? usedEffects;
const jobSlot = damageJobSlot(job);
applyBucketEffects(
options.effectIndex.damageGroupsByJobShape[jobSlot],
options.effectIndex.liveEffectsByGroup,
@@ -148,26 +208,12 @@
recording,
usedEffects,
primaryUsedEffects
);
-
- const damageBeforeOffsets = evaluateDamageExpressionForJob(job, buckets, staticProfile);
- let offsetDamage = sumSlots(buckets.factors, DYNAMIC_EXPRESSIONS[job.kind].postSubtractSlots);
- offsetDamage += applyTurnShield(
- buckets,
- job.round,
- Math.max(0, damageBeforeOffsets - offsetDamage),
- recording,
- usedEffects,
- primaryUsedEffects
- );
- const unroundedDamage = Math.max(0, damageBeforeOffsets - offsetDamage);
- const rawDamage = unroundedDamage;
- const uncappedKills = rawDamage;
- const kills = options.capToTakerTroops === false ? uncappedKills : Math.min(takerTroops, uncappedKills);
- return recording.finish({ job, factors: buckets.factors, armyTerm, damageBeforeOffsets, offsetDamage, rawDamage, kills });
+ return armyTerm;
}
+
function applyBucketEffects(
groups: ActiveEffectGroup[],
liveEffectsByGroup: ActiveEffect[][],
round: number,
@@ -177,44 +223,103 @@
usedEffects: ActiveEffect[],
primaryUsedEffects: ActiveEffect[]
): void {
for (const group of groups) {
- const dependency = group.requiredGroupOrdinalsByJobShape;
- if (dependency && !requiredEffectIsApplicable(dependency, liveEffectsByGroup, round, jobSlot)) continue;
- const effects = liveEffectsByGroup[group.ordinal];
- if (effects.length === 0) continue;
- if (group.sameEffectStacking !== "max") {
- for (const effect of effects) {
- if (!advanceEffectAttackDelay(effect)) continue;
- applyBucketEffect(effect, round, buckets, recording, usedEffects, primaryUsedEffects);
- }
- continue;
- }
- if (effects.length === 1) {
- const effect = effects[0];
- if (advanceEffectAttackDelay(effect)) {
- applyBucketEffect(effect, round, buckets, recording, usedEffects, primaryUsedEffects);
- }
- continue;
- }
- let selected: ActiveEffect | undefined;
- let selectedValue = Number.NEGATIVE_INFINITY;
- const eligible: ActiveEffect[] = [];
+ applyEffectGroup(group, liveEffectsByGroup, round, jobSlot, buckets, recording, usedEffects, primaryUsedEffects);
+ }
+}
+
+function applyEffectGroup(
+ group: ActiveEffectGroup,
+ liveEffectsByGroup: ActiveEffect[][],
+ round: number,
+ jobSlot: number,
+ buckets: NumericDamageBuckets,
+ recording: DamageJobRecorder,
+ usedEffects: ActiveEffect[],
+ primaryUsedEffects: ActiveEffect[]
+): void {
+ const dependency = group.requiredGroupOrdinalsByJobShape;
+ 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) {
if (!advanceEffectAttackDelay(effect)) continue;
- eligible.push(effect);
- const appliedValue = effect.getCurrentValue(round);
- if (!selected || appliedValue > selectedValue) {
- selected = effect;
- selectedValue = appliedValue;
- }
+ applyBucketEffect(effect, round, buckets, recording, usedEffects, primaryUsedEffects);
}
- if (selected) {
- applyBucketEffectGroup(selected, eligible, selectedValue, buckets, recording, usedEffects, primaryUsedEffects);
+ return;
+ }
+ if (effects.length === 1) {
+ const effect = effects[0];
+ if (advanceEffectAttackDelay(effect)) {
+ applyBucketEffect(effect, round, buckets, recording, usedEffects, primaryUsedEffects);
}
+ return;
}
+ let selected: ActiveEffect | undefined;
+ let selectedValue = Number.NEGATIVE_INFINITY;
+ const eligible: ActiveEffect[] = [];
+ for (const effect of effects) {
+ if (!advanceEffectAttackDelay(effect)) continue;
+ eligible.push(effect);
+ const appliedValue = effect.getCurrentValue(round);
+ if (!selected || appliedValue > selectedValue) {
+ selected = effect;
+ selectedValue = appliedValue;
+ }
+ }
+ if (selected) {
+ applyBucketEffectGroup(selected, eligible, selectedValue, buckets, recording, usedEffects, primaryUsedEffects);
+ }
}
+function applyShields(
+ job: DamageJob,
+ index: EffectIndex,
+ buckets: NumericDamageBuckets,
+ recording: DamageJobRecorder,
+ damage: number,
+ healthFactor: number,
+ usedEffects: ActiveEffect[],
+ primaryUsedEffects: ActiveEffect[]
+): number {
+ if (index.shields.length === 0) return 0;
+ const jobSlot = damageJobSlot(job);
+ let previousGroup: ActiveEffectGroup | undefined;
+ // Flat offsets precede consumable protection, as in the damage equation.
+ for (const shield of index.shields) {
+ const group = shield.effectGroup!;
+ if (group === previousGroup) continue;
+ previousGroup = group;
+ if (isTurnShield(shield) || !shieldEffectApplies(shield, job)) continue;
+ applyEffectGroup(group, index.liveEffectsByGroup, job.round, jobSlot, buckets, recording, usedEffects, primaryUsedEffects);
+ }
+ let offsetDamage = sumSlots(buckets.factors, DYNAMIC_EXPRESSIONS[job.kind].postSubtractSlots);
+ previousGroup = undefined;
+ for (const shield of index.shields) {
+ const group = shield.effectGroup!;
+ if (group === previousGroup) continue;
+ previousGroup = group;
+ if (!isTurnShield(shield) || !shieldEffectApplies(shield, job)) continue;
+ buckets.turnShield = undefined;
+ buckets.turnShieldGroup = undefined;
+ if (group.sameEffectStacking === "max") {
+ applyEffectGroup(group, index.liveEffectsByGroup, job.round, jobSlot, buckets, recording, usedEffects, primaryUsedEffects);
+ offsetDamage += applyTurnShield(buckets, job.round, Math.max(0, damage - offsetDamage), healthFactor, recording, usedEffects, primaryUsedEffects);
+ } else {
+ const dependency = group.requiredGroupOrdinalsByJobShape;
+ if (dependency && !requiredEffectIsApplicable(dependency, index.liveEffectsByGroup, job.round, jobSlot)) continue;
+ for (const effect of index.liveEffectsByGroup[group.ordinal]) {
+ if (!advanceEffectAttackDelay(effect)) continue;
+ buckets.turnShield = effect;
+ offsetDamage += applyTurnShield(buckets, job.round, Math.max(0, damage - offsetDamage), healthFactor, recording, usedEffects, primaryUsedEffects);
+ }
+ }
+ }
+ return offsetDamage;
+}
+
function requiredEffectIsApplicable(
dependency: Array<number[] | undefined>,
liveEffectsByGroup: ActiveEffect[][],
round: number,
@@ -306,28 +411,32 @@
function applyTurnShield(
buckets: NumericDamageBuckets,
round: number,
damage: number,
+ generatedHealthFactor: number,
recording: DamageJobRecorder,
usedEffects: ActiveEffect[],
primaryUsedEffects: ActiveEffect[]
): number {
const selected = buckets.turnShield;
if (!selected || damage <= 0) return 0;
- const appliedValue = Math.min(selected.getCurrentValue(round), damage);
+ // Source-attack budgets exclude runtime health modifiers; incoming casualties include them.
+ const healthFactor = selected.intent.value_formula?.source === "trigger.source_attack" ? generatedHealthFactor : 1;
+ const budgetUsed = Math.min(selected.getCurrentValue(round), damage * healthFactor);
+ const appliedValue = Math.min(damage, budgetUsed / healthFactor);
if (appliedValue <= 0) return 0;
applySelectedBucket(selected, appliedValue, buckets, recording);
const group = buckets.turnShieldGroup;
if (!group) {
- selected.initialValue = Math.max(0, selected.initialValue - appliedValue);
+ selected.initialValue = Math.max(0, selected.initialValue - budgetUsed);
usedEffects.push(selected);
if (primaryUsedEffects !== usedEffects && selected.triggerEffects?.length) primaryUsedEffects.push(selected);
return appliedValue;
}
for (const effect of group) {
- effect.initialValue = Math.max(0, effect.initialValue - appliedValue);
+ effect.initialValue = Math.max(0, effect.initialValue - budgetUsed);
usedEffects.push(effect);
if (effect !== selected) recording.recordRejected(effect, "same_effect_max_suppressed");
}
if (primaryUsedEffects !== usedEffects && selected.triggerEffects?.length) primaryUsedEffects.push(selected);
Index: simulator/src/effectIndex.test.ts
===================================================================
--- simulator/src/effectIndex.test.ts prev run
+++ simulator/src/effectIndex.test.ts this run
@@ -51,9 +51,9 @@
assert.deepEqual(index.damageGroupsByJobShape[damageJobSlot(job())].flatMap((group) => index.liveEffectsByGroup[group.ordinal]), [active]);
});
-test("expiring a modifier swap-removes it once from its shared job-shape group", () => {
+test("expiring a modifier preserves the remaining candidates in its shared group", () => {
const first = effect("active.hero.lethality.up");
const second = effect("active.hero.lethality.up");
second.intent = first.intent;
const index = preparedIndex([first, second]);
@@ -64,12 +64,32 @@
assert.deepEqual(index.liveEffectsByGroup[group.ordinal], [first, second]);
expireEffectIndex(index, first);
assert.deepEqual(index.liveEffectsByGroup[group.ordinal], [second]);
- assert.equal(second.effectGroupPosition, 0);
- assert.equal(first.effectGroup, undefined);
+ expireEffectIndex(index, first);
+ assert.deepEqual(index.liveEffectsByGroup[group.ordinal], [second]);
});
+test("expiring shields preserves other live shields and their dependency candidates", () => {
+ const first: ActiveEffect = { ...effect("active.hero.shield"), kind: "shield" };
+ const second: ActiveEffect = { ...effect("active.hero.shield"), kind: "shield" };
+ const index = preparedIndex([first, second]);
+ const group = first.effectGroup!;
+ indexEffect(index, first);
+ indexEffect(index, second);
+ assert.deepEqual(index.shields, [first, second]);
+ assert.deepEqual(index.liveEffectsByGroup[group.ordinal], [first, second]);
+
+ expireEffectIndex(index, first);
+ expireEffectIndex(index, first);
+ assert.deepEqual(index.shields, [second]);
+ assert.deepEqual(index.liveEffectsByGroup[group.ordinal], [second]);
+
+ expireEffectIndex(index, second);
+ assert.deepEqual(index.shields, []);
+ assert.deepEqual(index.liveEffectsByGroup[group.ordinal], []);
+});
+
function effect(type: string): ActiveEffect {
return {
source: { kind: "hero_skill", side: "attacker", effectId: type },
intent: { id: type, type, value: 25 },
@@ -105,9 +125,11 @@
sameEffectStacking: effect.sameEffectStacking
};
byResolvedGroup.set(key, group);
groups.push(group);
- for (const slot of slots) byShape[slot].push(group);
+ if (effect.kind !== "shield") {
+ for (const slot of slots) byShape[slot].push(group);
+ }
}
effect.effectGroup = group;
}
return createEffectIndex(groups, byShape);
Index: simulator/src/effectIndex.ts
===================================================================
--- simulator/src/effectIndex.ts prev run
+++ simulator/src/effectIndex.ts this run
@@ -1,6 +1,6 @@
import type { ActiveEffect, ActiveEffectGroup, DamageJob, DamageKind, SideId, UnitType } from "./types";
-import { unitsFromMask } from "./types";
+import { unitMaskHas, unitsFromMask } from "./types";
import { DYNAMIC_BUCKET_INDEX, dynamicBucketDefinition, type DynamicDamageBucket } from "./damageBuckets";
/**
* Runtime lookup structure for live effects, designed around the damage hot path.
@@ -18,9 +18,9 @@
* - A group is also the unit of same-effect max stacking: the max is selected among the
* group's live effects, and preparation asserts that a max-stacking definition's
* differently-scoped groups never overlap on a slot.
*
- * Control, extra-attack, and battle-order effects are rare and cheap; they stay in flat
+ * Shield, control, extra-attack, and battle-order effects are rare and cheap; they stay in flat
* lists matched per use. This split was benchmarked, not assumed — the group index beat a
* per-job classifier scan by ~3x on long battles while staying byte-identical on parity.
*
* Group descriptors and the two group arrays belong to the prepared battle and are
@@ -31,8 +31,9 @@
damageGroupsByJobShape: ActiveEffectGroup[][];
effectGroups: ActiveEffectGroup[];
// This run's live activations, parallel to effectGroups (indexed by group.ordinal).
liveEffectsByGroup: ActiveEffect[][];
+ shields: ActiveEffect[];
controls: ActiveEffect[];
extraAttacks: ActiveEffect[];
battleOrder: ActiveEffect[];
carriers: ActiveEffect[];
@@ -48,8 +49,9 @@
return {
damageGroupsByJobShape,
effectGroups,
liveEffectsByGroup: effectGroups.map(() => []),
+ shields: [],
controls: [],
extraAttacks: [],
battleOrder: [],
carriers: []
@@ -80,8 +82,18 @@
const live = index.liveEffectsByGroup[group.ordinal];
effect.effectGroupPosition = live.length;
live.push(effect);
effect.bucketIndex = group.bucketIndex;
+ if (effect.kind === "shield") {
+ // Keep prepared group order; liveEffectsByGroup retains stacking/dependency identity.
+ let position = index.shields.length;
+ index.shields.push(effect);
+ while (position > 0 && index.shields[position - 1].effectGroup!.ordinal > group.ordinal) {
+ index.shields[position] = index.shields[position - 1];
+ position -= 1;
+ }
+ index.shields[position] = effect;
+ }
}
export function isRuntimeIndexableEffect(effect: ActiveEffect): boolean {
if (effect.kind === "control" || effect.kind === "extra_attack" || effect.kind === "battle_order" || effect.kind === "carrier") return true;
@@ -93,11 +105,20 @@
if (effect.kind === "control") removeStable(index.controls, effect);
else if (effect.kind === "extra_attack") removeStable(index.extraAttacks, effect);
else if (effect.kind === "battle_order") removeStable(index.battleOrder, effect);
else if (effect.kind === "carrier") removeStable(index.carriers, effect);
+ else if (effect.kind === "shield") removeStable(index.shields, effect);
removeEffectGroupEntry(index, effect);
}
+export function shieldEffectApplies(effect: ActiveEffect, job: DamageJob): boolean {
+ return effect.appliesTo.side === job.takerSide &&
+ unitMaskHas(effect.appliesTo.units, job.takerUnit) &&
+ effect.appliesVs.side === job.dealerSide &&
+ unitMaskHas(effect.appliesVs.units, job.dealerUnit) &&
+ (effect.intent.applies_to_damage_kinds === undefined || effect.intent.applies_to_damage_kinds.includes(job.kind));
+}
+
export function damageJobSlot(job: DamageJob): number {
return damageJobShapeSlot(job.kind, job.dealerSide, job.dealerUnit, job.takerSide, job.takerUnit);
}
Index: simulator/src/extraAttacks.ts
===================================================================
--- simulator/src/extraAttacks.ts prev run
+++ simulator/src/extraAttacks.ts this run
@@ -8,9 +8,9 @@
UnitType
} from "./types";
import { UNIT_TYPES, unitMaskHas, unitsFromMask } from "./types";
import { advanceEffectAttackDelay } from "./effects";
-import { calculateDamageJob, type DamageJobOptions } from "./damage";
+import { generateDamageJob, deliverDamageJob, retainGeneratedDamage, ceilIgnoringFloatResidue, type DamageJobOptions } from "./damage";
import { normalizeUnitType } from "./normalize";
import {
capJobToRemainingTarget,
chargeEffectUse,
@@ -25,9 +25,9 @@
// 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;
-// that fixed result is emitted only when the child's locked normal attack matches.
+// shields are settled only when the child's locked normal attack matches.
// 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.
export function processExtraAttacks(
@@ -57,36 +57,39 @@
for (const pending of effect.pendingDamageJobs) {
const deliveryJob: DamageJob = {
...pending.job,
round,
- calculationRound: pending.job.round
+ calculationRound: pending.job.round,
+ roundStartTroops
};
if (loopOptions.capRoundKills && targetExhausted(deliveryJob, roundStartTroops, roundTargetDamage)) continue;
recorder.recordScheduledDamageJob(deliveryJob);
+ const result = deliverDamageJob(deliveryJob, pending.generated, damageJobOptions);
if (loopOptions.capRoundKills) {
- capJobToRemainingTarget(pending.result, deliveryJob, roundStartTroops, roundTargetDamage, recorder);
+ capJobToRemainingTarget(result, deliveryJob, roundStartTroops, roundTargetDamage, recorder);
} else if (loopOptions.capJobKills) {
- pending.result.kills = Math.min(
- pending.result.kills,
+ result.kills = Math.min(
+ result.kills,
Math.max(0, roundStartTroops[deliveryJob.takerSide][deliveryJob.takerUnit] ?? 0)
);
- recorder.recordFinalKills(pending.result);
+ recorder.recordFinalKills(result);
}
- results.push({ job: deliveryJob, result: pending.result, intent });
- totalKills += pending.result.kills;
+ results.push({ job: deliveryJob, result, intent });
+ totalKills += result.kills;
processedJobCount += 1;
firstProcessedJob ??= deliveryJob;
+ chargeUsedEffectsForJob(runtime, deliveryJob, recorder);
}
} 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 ((roundStartTroops[source.side][source.unit] ?? 0) <= 0) continue;
+ if (ceilIgnoringFloatResidue(roundStartTroops[source.side][source.unit] ?? 0) <= 0) continue;
for (const target of targets) {
- if ((roundStartTroops[target.side][target.unit] ?? 0) <= 0) continue;
+ if (ceilIgnoringFloatResidue(roundStartTroops[target.side][target.unit] ?? 0) <= 0) continue;
const job: DamageJob = {
round,
kind: definition.damage_kind ?? "skill",
roundStartTroops,
@@ -98,9 +101,10 @@
sourceMultiplier: multiplier
};
if (loopOptions.capRoundKills && targetExhausted(job, roundStartTroops, roundTargetDamage)) continue;
recorder.recordScheduledDamageJob(job);
- const result = calculateDamageJob(job, fighters, damageJobOptions);
+ 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);
}
@@ -142,11 +146,11 @@
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 source of sources) {
- if ((roundStartTroops[source.side][source.unit] ?? 0) <= 0) continue;
+ if (ceilIgnoringFloatResidue(roundStartTroops[source.side][source.unit] ?? 0) <= 0) continue;
for (const target of targets) {
- if ((roundStartTroops[target.side][target.unit] ?? 0) <= 0) continue;
+ if (ceilIgnoringFloatResidue(roundStartTroops[target.side][target.unit] ?? 0) <= 0) continue;
const job: DamageJob = {
round,
kind: definition.damage_kind ?? "skill",
roundStartTroops,
@@ -156,10 +160,10 @@
takerUnit: target.unit,
sourceEffectId: effect.source.effectId ?? effect.intent.id,
sourceMultiplier: multiplier
};
- const result = calculateDamageJob(job, fighters, { ...damageJobOptions, capToTakerTroops: false });
- pending.push({ job, result });
+ const generated = retainGeneratedDamage(generateDamageJob(job, fighters, damageJobOptions));
+ pending.push({ job, generated });
chargeUsedEffectsForJob(runtime, job, damageJobOptions.recorder);
}
}
}
@@ -202,9 +206,9 @@
return units.map((unit) => ({ side: fallbackSide, unit }));
}
function livingUnits(side: SideId, roundStartTroops: DamageJob["roundStartTroops"]): TriggerJobUnit[] {
- return UNIT_TYPES.filter((unit) => (roundStartTroops[side][unit] ?? 0) > 0).map((unit) => ({ side, unit }));
+ return UNIT_TYPES.filter((unit) => ceilIgnoringFloatResidue(roundStartTroops[side][unit] ?? 0) > 0).map((unit) => ({ side, unit }));
}
function unitListFromSelector(selector: TriggerDamageJobSelector): UnitType[] | undefined {
if (Array.isArray(selector)) {
Index: simulator/src/recorder.ts
===================================================================
--- simulator/src/recorder.ts prev run
+++ simulator/src/recorder.ts this run
@@ -55,8 +55,9 @@
}
/** Per-damage-job observation sink selected by the enclosing battle recorder. */
export interface DamageJobRecorder {
+ readonly needsFactors: boolean;
recordEffect(effect: ActiveEffect, value: number): void;
recordRejected(effect: ActiveEffect, reason: RejectedEffectReason): void;
finish(result: DamageRecordingResult): DamageResult;
}
@@ -121,8 +122,9 @@
const NO_APPLIED_EFFECTS: AppliedEffect[] = [];
const EMPTY_SKILL_REPORT: Record<SideId, SkillReportEntry[]> = { attacker: [], defender: [] };
const NULL_DAMAGE_JOB_RECORDER: DamageJobRecorder = {
+ needsFactors: false,
recordEffect() {},
recordRejected() {},
finish({ kills }) {
return { kills };
@@ -390,8 +392,9 @@
}
}
class BasicDamageJobRecorder implements DamageJobRecorder {
+ readonly needsFactors = false;
private readonly appliedEffects: AppliedEffect[] = [];
constructor(private readonly staticApplied?: StaticAppliedIndex) {}
@@ -409,8 +412,9 @@
}
}
class FullDamageJobRecorder implements DamageJobRecorder {
+ readonly needsFactors = true;
private readonly appliedEffects: DamageEquationTrace["appliedEffects"] = [];
private readonly contributors: DamageBucketTrace["contributors"][] = Array.from(
{ length: DYNAMIC_BUCKETS.length },
() => []
Index: simulator/src/runtime.ts
===================================================================
--- simulator/src/runtime.ts prev run
+++ simulator/src/runtime.ts this run
@@ -16,9 +16,9 @@
skillMatchesTrigger,
type Rng
} from "./effects";
import { createEffectIndex, expireEffectIndex, indexEffect, isRuntimeIndexableEffect, type EffectIndex } from "./effectIndex";
-import { createDamageScratch, type DamageResult, type DamageScratch, type StaticDamageProfile } from "./damage";
+import { ceilIgnoringFloatResidue, createDamageScratch, type DamageResult, type DamageScratch, type StaticDamageProfile } from "./damage";
import type { DeferredEffectPlan, PreparedAttackSkill, RuntimeSkills } from "./runtimeSkills";
import type { BattleRecorder } from "./recorder";
import { emptyTroops } from "./fighterResolution";
@@ -238,9 +238,9 @@
roundStartTroops: DamageJob["roundStartTroops"],
roundTargetDamage: Record<SideId, Record<UnitType, number>>
): boolean {
const available = Math.max(0, roundStartTroops[job.takerSide][job.takerUnit] ?? 0);
- return available <= 0 || roundTargetDamage[job.takerSide][job.takerUnit] >= available;
+ return ceilIgnoringFloatResidue(Math.max(0, available - roundTargetDamage[job.takerSide][job.takerUnit])) === 0;
}
export function capJobToRemainingTarget(
result: DamageResult,
Index: simulator/src/runtimeSkills.ts
===================================================================
--- simulator/src/runtimeSkills.ts prev run
+++ simulator/src/runtimeSkills.ts this run
@@ -160,8 +160,17 @@
intent.effectGroupsByScopeKey = groupsByScopeKey;
}
}
linkRequiredEffectGroups(effectGroups, damageGroupsByJobShape, groupMetadata);
+ // Dependencies retain group identity, but shield delivery uses the small live list.
+ const heroShield = damageBucketIndex("active.hero.shield");
+ const troopShield = damageBucketIndex("active.troop.shield");
+ for (const groups of damageGroupsByJobShape) {
+ for (let i = groups.length - 1; i >= 0; i -= 1) {
+ const bucket = groups[i].bucketIndex;
+ if (bucket === heroShield || bucket === troopShield) groups.splice(i, 1);
+ }
+ }
return {
preBattle,
battleStart,
roundStart,
Index: simulator/src/simulator.test.ts
===================================================================
--- simulator/src/simulator.test.ts prev run
+++ simulator/src/simulator.test.ts this run
@@ -20,8 +20,84 @@
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" });
+
+ 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("delayed damage settles shields on arrival 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"));
+ assert.equal(signedRemainingScore(runOnce(input, config)), survivors, path);
+ }
+});
+
+test("exhausted infantry cannot shield marksmen through floating-point residue", () => {
+ const [input] = JSON.parse(readFileSync(new URL("../../testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_5.json", import.meta.url), "utf8"));
+ const config = loadSimulatorConfig();
+ delete config.heroDefinitions.Gwen.skills.AirDominance.effects["AirDominance/1"].trigger_effects!["AirDominance/2"].units!.applies_vs;
+ const result = runOnce(input, config, { mode: "standard" });
+ const incoming = result.attacks.filter(attack => attack.round === 15 && attack.dealerSide === "defender");
+ assert.deepEqual(incoming.map(attack => attack.takerUnit), ["marksman", "marksman"]);
+ assert.equal(signedRemainingScore(result), 383);
+});
+
+test("same-turn exhausted targets cannot receive phantom extra skill hits", () => {
+ const [input] = JSON.parse(readFileSync(new URL("../../testcases/emulator_verified/alonso_norah_combo.json", import.meta.url), "utf8"));
+ const seed = "simulator-default:testcases/emulator_verified/alonso_norah_combo.json:alonso_norah_combo:0:264";
+ const result = runPrepared(prepareBattle(input, loadSimulatorConfig()), seed, { mode: "standard" });
+ const phantomHits = result.attacks.filter(attack =>
+ attack.round === 10 && attack.kind === "skill" && attack.dealerSide === "defender" &&
+ attack.dealerUnit === "marksman" && attack.takerUnit === "infantry"
+ );
+ assert.deepEqual(phantomHits, []);
+});
+
test("runPrepared reuses the compiled input seed when no override is supplied", () => {
const config = loadSimulatorConfig();
const stats = {
inf: { attack: 200, defense: 200, lethality: 150, health: 150 },
@@ -84,8 +160,44 @@
assert.equal(bothEmpty.rounds, 0);
assert.equal(bothEmpty.attacks.length, 0);
});
+test("Gwen preparation cycles match recorded survivors and skill counts", () => {
+ const path = fileURLToPath(new URL("../testcases/emulator_verified/gwen_blastmaster_cadence_800m_vs_1200i_100l_100m.json", import.meta.url));
+ const [input] = JSON.parse(readFileSync(path, "utf8")) as BattleInput[];
+ const result = runOnce(input, loadSimulatorConfig());
+
+ assert.deepEqual(result.remaining, {
+ attacker: { infantry: 0, lancer: 0, marksman: 0 },
+ defender: { infantry: 461, lancer: 58, marksman: 44 }
+ });
+ assert.equal(result.skillReport.attacker.find((skill) => skill.skillId === "AirDominance")?.skillActivations, 7);
+ assert.equal(result.skillReport.attacker.find((skill) => skill.skillId === "Blastmaster")?.skillActivations, 9);
+});
+
+test("Ahmose counts four Infantry attacks without counting pauses or stuns", () => {
+ const result = runOnce(
+ {
+ maxRounds: 12,
+ attacker: { troops: { infantry_t6: 1000, lancer_t6: 1000 }, heroes: { Ahmose: { skill_1: 1 } } },
+ defender: { troops: { lancer_t6: 1000 }, heroes: { Sonya: { skill_3: 1 } } }
+ },
+ loadSimulatorConfig()
+ );
+ const infantry = result.attacks.filter(
+ (attack) => attack.dealerSide === "attacker" && attack.dealerUnit === "infantry" && attack.kind === "normal"
+ );
+
+ assert.deepEqual(
+ infantry.filter((attack) => !attack.cancelReason).map((attack) => attack.round),
+ [1, 2, 3, 4, 7, 8, 9, 10, 12]
+ );
+ assert.deepEqual(
+ infantry.filter((attack) => attack.cancelReason === "no_attack").map((attack) => attack.round),
+ [5, 6, 11]
+ );
+});
+
test("runPrepared returns structured result for a no-hero battle", () => {
const config = loadSimulatorConfig();
const result = runOnce(
{
@@ -659,16 +771,15 @@
for (const fixture of [
{ id: "ahmose_no_infantry_240l_vs_125i", attacker: { infantry: 0, lancer: 0, marksman: 0 }, defender: { infantry: 11, lancer: 0, marksman: 0 } },
{ id: "ahmose_source_dies_001i_480l_vs_250i", attacker: { infantry: 0, lancer: 3, marksman: 0 }, defender: { infantry: 0, lancer: 0, marksman: 0 } }
]) {
- test(`Ahmose Viper schedules unused pauses without protection: ${fixture.id}`, () => {
+ test(`Ahmose earns no protection without four Infantry attacks: ${fixture.id}`, () => {
const path = new URL(`../testcases/emulator_verified/${fixture.id}.json`, import.meta.url);
const [input] = JSON.parse(readFileSync(path, "utf8")) as BattleInput[];
const result = runOnce(input, loadSimulatorConfig());
assert.equal(result.randomness.deterministic, true);
assert.deepEqual(result.remaining, { attacker: fixture.attacker, defender: fixture.defender });
- assert.equal(result.skillReport.attacker.find(entry => entry.skillId === "ViperFormation")!.skillActivations, Math.floor(result.rounds / 4));
assert.equal(result.attackControlCounts.no_attack, 0);
});
}
@@ -1753,8 +1864,54 @@
const uncharged = roundTwoKills({ useEffectsOnNoAttack: false });
assert.ok(uncharged > charged, `expected uncharged buff to boost round 2 (${uncharged} > ${charged})`);
});
+test("cancelled incoming attacks charge attack-limited shields only when cancellation charging is enabled", () => {
+ const input: BattleInput = {
+ maxRounds: 2,
+ seed: "cancel-shield-charge",
+ attacker: { troops: { infantry_t1: 1000 }, heroes: {} },
+ defender: { troops: { infantry_t1: 1000 }, heroes: { ShieldAndPause: { skill_1: 1 } } }
+ };
+ const config = minimalConfig({
+ ShieldAndPause: {
+ name: "ShieldAndPause",
+ skills: {
+ OpeningProtection: {
+ trigger: { type: "battle_start" },
+ effects: {
+ pause: {
+ type: "no_attack",
+ value: 100,
+ units: { applies_to: "enemy.infantry", applies_vs: "self.infantry" },
+ duration: { turns: { count: 1, delay: 1 } }
+ },
+ shield: {
+ type: "active.hero.shield",
+ value: 100,
+ units: { applies_to: "self.infantry", applies_vs: "enemy.infantry" },
+ duration: { attacks: { count: 1 } }
+ }
+ }
+ }
+ }
+ }
+ });
+ const [charged, preserved] = [{}, { useEffectsOnNoAttack: false }].map((options) =>
+ runOnce(input, config, { ...options, mode: "trace" }).attacks.filter(
+ (attack) => attack.dealerSide === "attacker" && attack.kind === "normal"
+ )
+ );
+ assert.equal(charged[0].cancelReason, "no_attack");
+ assert.equal(preserved[0].cancelReason, "no_attack");
+ assert.equal(charged[1].round, 2);
+ assert.equal(preserved[1].round, 2);
+ assert.ok(charged[1].kills > 0);
+ assert.equal(preserved[1].kills, 0);
+ assert.equal((charged[1].appliedEffects ?? []).some((applied) => hasEffectKind(applied, "shield")), false);
+ assert.equal(preserved[1].appliedEffects?.some((applied) => hasEffectKind(applied, "shield")), true);
+});
+
test("no_attack does not advance normal attack cadence counters", () => {
const result = runOnce(
{
maxRounds: 4,
@@ -2929,8 +3086,71 @@
);
assert.deepEqual(result.extraSkillAttackJobsByEffect, { hitAgain: 1 });
});
+for (const delayed of [false, true]) {
+ test(`extra hits apply target modifiers to each actual recipient (${delayed ? "captured" : "immediate"})`, () => {
+ const splash: Omit<EffectIntentDefinition, "id"> = {
+ type: "extra_skill_attack",
+ value: 100,
+ units: {
+ applies_to: delayed ? "parent.use.source" : "self.marksman",
+ applies_vs: delayed ? "parent.use.target" : "enemy.any"
+ },
+ trigger_damage_jobs: [{ source: "use.source", target: "enemy.living" }],
+ duration: { turns: { delay: delayed ? 1 : 0, count: 1 }, attacks: { count: 1 } }
+ };
+ const result = runOnce(
+ {
+ maxRounds: delayed ? 2 : 1,
+ attacker: { troops: { marksman_t1: 1000000 }, heroes: { MultiTarget: { skill_1: 1, skill_2: 1 } } },
+ defender: { troops: { infantry_t1: 1000000, lancer_t1: 1000000, marksman_t1: 1000000 }, heroes: {} }
+ },
+ minimalConfig({
+ MultiTarget: {
+ name: "MultiTarget",
+ troop_type: "marksman",
+ skills: {
+ 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" } }
+ }
+ },
+ Splash: {
+ trigger: { type: "turn", first: 1, every: 10 },
+ effects: delayed ? {
+ carrier: {
+ units: { applies_to: "self.marksman", applies_vs: "enemy.any" },
+ duration: { turns: { count: 1 }, attacks: { count: 1 } },
+ trigger_effects: { splash }
+ }
+ } : { splash }
+ }
+ }
+ }
+ })
+ );
+ const parent = result.attacks.find(attack =>
+ attack.round === 1 && attack.dealerSide === "attacker" && attack.kind === "normal"
+ );
+ 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 };
+ 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`
+ );
+ }
+ });
+}
+
test("extra skill trigger damage jobs can resolve to multiple living enemy targets without recursive attack triggers", () => {
const result = runOnce(
{
maxRounds: 1,
Index: simulator/src/simulator.ts
===================================================================
--- simulator/src/simulator.ts prev run
+++ simulator/src/simulator.ts this run
@@ -14,9 +14,9 @@
SimulatorConfig,
UnitType
} from "./types";
import { UNIT_TYPES, unitMaskHas } from "./types";
-import { calculateDamageJob, ceilIgnoringFloatResidue, minInitialArmy, type DamageResult } from "./damage";
+import { generateDamageJob, deliverDamageJob, ceilIgnoringFloatResidue, minInitialArmy, type DamageResult } from "./damage";
import { createRecorder, type BattleRecorder } from "./recorder";
import {
activateEffect,
advanceEffectAttackDelay,
@@ -25,9 +25,9 @@
isEffectAttackReady,
oppositeSide,
type Rng
} from "./effects";
-import { damageJobShapeSlot, damageJobSlot, type EffectIndex } from "./effectIndex";
+import { damageJobShapeSlot, damageJobSlot, shieldEffectApplies, type EffectIndex } from "./effectIndex";
import { buildRuntimeSkills, type RuntimeSkills } from "./runtimeSkills";
import { activatePreBattleEffects, buildResolved, prepareBattle, type CompiledBattle } from "./prepare";
import {
addActiveEffect,
@@ -262,9 +262,9 @@
const orderIndexBySide: Record<SideId, number> = { attacker: 0, defender: 0 };
for (const dealerUnit of UNIT_TYPES) {
for (const side of ["attacker", "defender"] as SideId[]) {
const takerSide = oppositeSide(side);
- if ((roundStartTroops[side][dealerUnit] ?? 0) <= 0) continue;
+ if (ceilIgnoringFloatResidue(roundStartTroops[side][dealerUnit] ?? 0) <= 0) continue;
const ordered = orderFromEffects(dealerUnit, side, runtime.effectIndex, true);
const takerUnit = firstLivingUnit(ordered?.order ?? UNIT_TYPES, takerSide, roundStartTroops);
if (!takerUnit) continue;
const intent = makeNormalIntent(
@@ -314,9 +314,10 @@
materializeTriggeredEffects(dodge.effect, round, intent, runtime, recorder);
if (useEffectsOnCancel.dodge) chargeCancelledAttack(job, dodge.effect, dodge.attackDurationEffects, runtime);
} else {
recorder.recordScheduledDamageJob(job);
- const normalResult = calculateDamageJob(job, fighters, damageJobOptions);
+ const generated = generateDamageJob(job, fighters, damageJobOptions);
+ const normalResult = deliverDamageJob(job, generated, damageJobOptions);
if (loopOptions.capRoundKills) capJobToRemainingTarget(normalResult, job, roundStartTroops, roundTargetDamage, recorder);
normalKills = normalResult.kills;
if (loopOptions.scoreSide && job.dealerSide === loopOptions.scoreSide.dealerSide && job.takerSide === loopOptions.scoreSide.takerSide) {
score += normalResult.kills;
@@ -445,9 +446,9 @@
};
}
function firstLivingUnit(order: readonly UnitType[], side: SideId, roundStartTroops: DamageJob["roundStartTroops"]): UnitType | undefined {
- return order.find((unit) => (roundStartTroops[side][unit] ?? 0) > 0);
+ return order.find((unit) => ceilIgnoringFloatResidue(roundStartTroops[side][unit] ?? 0) > 0);
}
function orderFromEffects(
dealerUnit: UnitType,
@@ -556,9 +557,10 @@
losses[job.takerSide][job.takerUnit] += result.kills;
}
for (const side of ["attacker", "defender"] as SideId[]) {
for (const unit of UNIT_TYPES) {
- runtime.troops[side][unit] = Math.max(0, runtime.troops[side][unit] - losses[side][unit]);
+ const remaining = Math.max(0, runtime.troops[side][unit] - losses[side][unit]);
+ runtime.troops[side][unit] = ceilIgnoringFloatResidue(remaining) === 0 ? 0 : remaining;
}
}
}
@@ -574,16 +576,20 @@
if (!advanceEffectAttackDelay(effect)) continue;
if (hasAttackDurationConstraint(effect)) used.push(effect);
}
}
+ for (const effect of runtime.effectIndex.shields) {
+ if (!shieldEffectApplies(effect, job) || !advanceEffectAttackDelay(effect)) continue;
+ if (hasAttackDurationConstraint(effect)) used.push(effect);
+ }
for (const effect of controlEffects) used.push(effect);
if (!controlEffects.includes(winningControl)) used.push(winningControl);
chargeUsedEffects(runtime);
}
function winnerFor(troops: Record<SideId, Record<UnitType, number>>): SideId | "draw" | undefined {
- const attackerAlive = total(troops.attacker) > 0;
- const defenderAlive = total(troops.defender) > 0;
+ const attackerAlive = UNIT_TYPES.some((unit) => ceilIgnoringFloatResidue(troops.attacker[unit]) > 0);
+ const defenderAlive = UNIT_TYPES.some((unit) => ceilIgnoringFloatResidue(troops.defender[unit]) > 0);
if (attackerAlive && !defenderAlive) return "attacker";
if (defenderAlive && !attackerAlive) return "defender";
if (!attackerAlive && !defenderAlive) return "draw";
return undefined;
Index: simulator/src/tooling/testcases.test.ts
===================================================================
--- simulator/src/tooling/testcases.test.ts prev run
+++ simulator/src/tooling/testcases.test.ts this run
@@ -1,6 +1,6 @@
import assert from "node:assert/strict";
-import { mkdirSync, writeFileSync } from "node:fs";
+import { mkdirSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { resolve } from "node:path";
import { test } from "node:test";
@@ -25,8 +25,46 @@
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");
+ 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: {} } } },
+ "unrelated.json": { description: "Gwen", attacker: { heroes: { Sergey: {} } } },
+ "disabled.json.disabled": { attacker: { heroes: { 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",
+ ];
+ assert.deepEqual(
+ 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"]
+ .map((file) => resolve(testcaseRoot, file)).sort(),
+ );
+ assert.deepEqual(
+ discoverTestcaseFiles({ testcaseRoot, matching: "unrelated" }),
+ [resolve(testcaseRoot, "unrelated.json")],
+ );
+ assert.deepEqual(discoverTestcaseFiles({ testcaseRoot, matching: "missing" }), []);
+});
+
test("runTestcases returns compact summary entries and full detail entries separately", () => {
const config = loadSimulatorConfig();
const report = runTestcases({ matching: "simple_001", repeat: 5 }, config);
const key = Object.keys(report.testcases)[0];
@@ -260,8 +298,73 @@
assert.notEqual(adjustment.unadjusted.mu_candidate, summary?.game?.mu_candidate);
assert.deepEqual(detail?.comparisonSamples, [summary?.game?.mu_candidate]);
});
+test("exact comparisons reject off-by-one and equal-mean outcomes but allow stat rounding correction", (t) => {
+ const testcaseRoot = tempDir("simulator-exact");
+ t.after(() => rmSync(testcaseRoot, { recursive: true, force: true }));
+ const file = resolve(testcaseRoot, "exact.json");
+ const entry = {
+ attacker: { troops: { infantry_t1: 1000 }, stats: { infantry: { attack: 100.001 } } },
+ defender: { troops: { infantry_t1: 900 } }
+ };
+ const config = testcaseFixtureConfig();
+ writeFileSync(file, JSON.stringify(entry));
+ const score = battleScoreDelta(runTestcases({ testcaseRoot }, config).details[0]!.result)!;
+ const outcome = (value: number) => ({ attacker: Math.max(0, value), defender: Math.max(0, -value) });
+ writeFileSync(file, JSON.stringify([
+ { ...entry, test_id: "same", game_report_result: outcome(score) },
+ { ...entry, test_id: "off_by_one", game_report_result: outcome(score + 1) },
+ { ...entry, test_id: "same_mean", game_report_result: [outcome(score - 1), outcome(score + 1)] }
+ ]));
+ const normal = runTestcases({ testcaseRoot }, config);
+ assert.ok(Object.values(normal.testcases).every((summary) => summary.game?.passes));
+ const exact = runTestcases({ testcaseRoot, exact: true }, config);
+ assert.deepEqual(Object.values(exact.testcases).map((summary) => summary.game?.passes), [true, false, false]);
+
+ const adjusted = runTestcases({ matching: "determinism_test_normal", exact: true }, loadSimulatorConfig());
+ const summary = Object.values(adjusted.testcases)[0]!;
+ assert.equal(summary.game?.passes, true);
+ assert.equal(summary.game?.bias_raw, 0);
+ assert.equal(summary.gameStatAdjustment?.mode, "deterministic_exact");
+});
+
+for (const preciseSide of ["attacker", "defender"] as const) {
+ test(`three-decimal ${preciseSide} stats keep game comparisons unadjusted`, (t) => {
+ const testcaseRoot = tempDir(`simulator-precise-stats-${preciseSide}`);
+ t.after(() => rmSync(testcaseRoot, { recursive: true, force: true }));
+ writeFileSync(resolve(testcaseRoot, "precise.json"), JSON.stringify({
+ attacker: {
+ troops: { infantry_t1: 1000 },
+ stats: { infantry: { attack: preciseSide === "attacker" ? 100.001 : 100.1 } }
+ },
+ defender: {
+ troops: { infantry_t1: 900 },
+ stats: { infantry: { health: preciseSide === "defender" ? 100.001 : 100.1 } }
+ },
+ game_report_result: { attacker: 264, defender: 0 }
+ }));
+ const report = runTestcases({ testcaseRoot, includeSamples: true }, testcaseFixtureConfig());
+ const summary = Object.values(report.testcases)[0]!;
+ const detail = report.details[0]!;
+
+ assert.notEqual(summary.game?.bias_raw, 0);
+ assert.equal(summary.gameStatAdjustment, undefined);
+ assert.equal(summary.game?.mu_candidate, battleScoreDelta(detail.result));
+ assert.deepEqual(detail.comparisonSamples, [battleScoreDelta(detail.result)]);
+ });
+}
+
+test("two-decimal rounding corrections recover exact no-hero outcomes within 0.005", () => {
+ const report = runTestcases({ matching: "1-testcases_no-heroes_t6_single-type", deterministic: true, exact: true }, loadSimulatorConfig());
+ const summaries = Object.values(report.testcases);
+ assert.equal(summaries.length, 18);
+ assert.ok(summaries.every((entry) => entry.game?.passes));
+ const corrected = summaries.filter((entry) => entry.gameStatAdjustment);
+ assert.equal(corrected.length, 11);
+ assert.ok(corrected.every((entry) => Math.abs(entry.gameStatAdjustment!.value) <= 0.005));
+});
+
test("deterministic testcase tolerance increases by 0.1 percent every ten rounds and caps at 0.7 percent", () => {
assert.equal(deterministicRoundTolerancePct(0), 0.2);
assert.equal(deterministicRoundTolerancePct(9), 0.2);
assert.equal(deterministicRoundTolerancePct(10), 0.3);
Index: simulator/src/tooling/testcases.ts
===================================================================
--- simulator/src/tooling/testcases.ts prev run
+++ simulator/src/tooling/testcases.ts this run
@@ -24,8 +24,10 @@
testcaseRoot?: string;
calibrationReportPath?: string;
matching?: string;
includeDisabled?: boolean;
+ deterministic?: boolean;
+ exact?: boolean;
repeat?: number;
seed?: string | number;
workers?: number;
includeSamples?: boolean;
@@ -150,13 +152,15 @@
repeat: number;
seed?: string | number;
includeSamples?: boolean;
simulationMode?: SimulationMode;
+ deterministic?: boolean;
}
export interface TestcaseExecutionResult {
testcaseId: string;
index: number;
+ skipped?: boolean;
result?: BattleResult;
deterministic?: boolean;
sampleCount?: number;
simulatorStats?: SampleStats;
@@ -198,12 +202,31 @@
walk(root, files);
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))
+ .filter((file) => !options.matching || file.includes(options.matching) || fileHeroesMatch(file, options.matching))
.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));
}
@@ -270,9 +293,10 @@
report.details.push(detail);
continue;
}
try {
- const execution = execute({ file, reportFile, testcaseId, index, input: preparedCase.input, repeat, seed: options.seed }, config);
+ const execution = execute({ file, reportFile, testcaseId, index, input: preparedCase.input, repeat, seed: options.seed, deterministic: options.deterministic }, config);
+ if (execution.skipped) continue;
applyExecutionResult(report, comparison, preparedCase, execution, config);
} catch (error) {
detail.error = errorMessage(error);
detail.errorDetails = errorDetails(error);
@@ -316,9 +340,10 @@
testcaseId: preparedCase.testcaseId,
index: preparedCase.index,
input: preparedCase.input,
repeat,
- seed: options.seed
+ seed: options.seed,
+ deterministic: options.deterministic
});
return { preparedCase, execution };
} catch (error) {
return { preparedCase, error };
@@ -326,8 +351,9 @@
});
for (const { preparedCase, execution, error } of await Promise.all(jobs)) {
const { reportFile, testcaseId, index, detail } = preparedCase;
+ if (execution?.skipped) continue;
if (!preparedCase.input || !preparedCase.key) {
if (preparedCase.adaptError) report.errors.push(preparedCase.adaptError);
report.details.push(detail);
continue;
@@ -388,10 +414,9 @@
thresholds: comparison.thresholds
})
: null;
if (game) {
- const unroundedBiasRaw = stats.mu - mean(gameSamples);
- game = adjustedForRoundingRules(game, result.randomness.deterministic, initialTroops, result.rounds, unroundedBiasRaw);
+ game = adjustedForRoundingRules(game, result.randomness.deterministic, initialTroops, result.rounds, simulatorSamples, gameSamples, report.options.exact);
}
const gameStatAdjustment = game && preparedCase.input
? findGameStatAdjustment({
game,
@@ -401,8 +426,9 @@
reference: gameSamples,
initialTroops,
averageRounds: result.rounds,
deterministic: result.randomness.deterministic,
+ exact: report.options.exact,
thresholds: comparison.thresholds
})
: undefined;
if (gameStatAdjustment) game = gameStatAdjustment.adjusted;
@@ -456,15 +482,27 @@
initialTroops: number;
averageRounds: number;
deterministic: boolean;
thresholds?: Record<string, number>;
+ exact?: boolean;
}): InternalStatAdjustment | undefined {
+ 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;
+ }
+ }
+ }
// 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;
const direction = -Math.sign(options.game.bias_raw);
- const maxCandidate = evaluateStatAdjustment(options, direction * STAT_ROUNDING_MAX_ADJUSTMENT);
+ const maxCandidate = evaluateStatAdjustment(options, direction * maxAdjustment);
let best = maxCandidate;
if (maxCandidate.mode === "deterministic_exact") return maxCandidate;
let low = { value: 0, bias: options.game.bias_raw };
@@ -500,8 +538,9 @@
initialTroops: number;
averageRounds: number;
deterministic: boolean;
thresholds?: Record<string, number>;
+ exact?: boolean;
}, value: number): InternalStatAdjustment {
const adjustedInput = inputWithStatAdjustment(options.input, value);
const candidateSamples = simulateAdjustedOutcomes(adjustedInput, options.job, options.config);
const adjusted = compareOutcomeDistribution({
@@ -523,9 +562,11 @@
adjusted,
options.deterministic,
options.initialTroops,
options.averageRounds,
- mean(candidateSamples) - mean(options.reference)
+ candidateSamples,
+ options.reference,
+ options.exact
),
samples: candidateSamples
};
}
@@ -585,12 +626,23 @@
metric: ParityComparisonMetrics,
deterministic: boolean,
initialTroops: number,
averageRounds: number,
- unroundedBiasRaw: number
+ candidate: readonly number[],
+ reference: readonly number[],
+ exact = false
): ParityComparisonMetrics {
+ if (exact) {
+ const expected = reference[0];
+ return {
+ ...metric,
+ passes: Number.isInteger(expected)
+ && reference.every((value) => value === expected)
+ && candidate.every((value) => value === expected)
+ };
+ }
if (!deterministic) return metric;
- const unroundedBiasPct = Math.abs(unroundedBiasRaw / (initialTroops || 1)) * 100;
+ const unroundedBiasPct = Math.abs((mean(candidate) - mean(reference)) / (initialTroops || 1)) * 100;
return { ...metric, passes: unroundedBiasPct <= deterministicRoundTolerancePct(averageRounds) };
}
export function deterministicRoundTolerancePct(averageRounds: number): number {
@@ -613,9 +665,9 @@
};
}
function roundStatAdjustment(value: number): number {
- return Number(value.toFixed(3));
+ return Number(value.toFixed(4));
}
function finalizeReport(report: TestcaseRunReport): void {
report.counts.warnings = report.warnings.length;
@@ -634,8 +686,11 @@
const sampleOutcomes: TestcaseSampleOutcome[] = [];
const sampleDeltas: number[] = [];
// Resolve the battle once and reuse it across every seeded sample of this case.
const compiled = prepareBattle(job.input, config);
+ if (job.deterministic && !compiled.runtimeSkills.randomness.deterministic) {
+ return { testcaseId: job.testcaseId, index: job.index, skipped: true, diagnostics: [] };
+ }
const baseSeed = job.seed ?? job.input.seed;
const sample = (iteration: number) => runPrepared(
compiled,
sampleSeed(baseSeed, job.file, job.testcaseId, job.index, iteration),
Index: simulator/src/types.ts
===================================================================
--- simulator/src/types.ts prev run
+++ simulator/src/types.ts this run
@@ -1,4 +1,6 @@
+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;
@@ -325,9 +327,9 @@
// unless useEffectsOnDodge/useEffectsOnNoAttack disable that.
uses: number;
sameEffectStacking: SameEffectStacking;
triggerEffects?: ResolvedEffectIntentDefinition[];
- /** Completed damage captured when this runtime effect was materialized and emitted on its later matching attack. */
+ /** Generation-time damage awaiting shield settlement on its later matching attack. */
pendingDamageJobs?: PendingDamageJob[];
effectGroup?: ActiveEffectGroup;
effectGroupPosition?: number;
}
@@ -375,9 +377,9 @@
}
export interface PendingDamageJob {
job: DamageJob;
- result: DamageResult;
+ generated: GeneratedDamage;
}
export interface CounterDelta {
side: SideId;
Index: testcases/emulator_verified/ahmose_blade_categories/ahmose_vs_jasser_01.json
===================================================================
--- testcases/emulator_verified/ahmose_blade_categories/ahmose_vs_jasser_01.json prev run
+++ testcases/emulator_verified/ahmose_blade_categories/ahmose_vs_jasser_01.json this run
@@ -0,0 +1,167 @@
+[
+ {
+ "test_id": "ahmose_vs_jasser_01",
+ "description": "C2 battle report 2734693171196384",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 900,
+ "marksman_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "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": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 800,
+ "lancer_t6": 150,
+ "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": 297.4,
+ "defense": 286.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-22T15:01:47.000Z",
+ "randomseed": 1790089308,
+ "attacker": 402,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171196384",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T15-02-04.099Z-66897957-94ec-40d9-b035-eb9f41c2b9ba.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 402,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 388,
+ "kills": 438
+ },
+ "defender": {
+ "troops": 1250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 438,
+ "lightly_injured": 812,
+ "kills": 210
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 900,
+ "survivors": 302,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 388,
+ "kills": 216
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 100,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 222
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 800,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 280,
+ "lightly_injured": 520,
+ "kills": 26
+ },
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 48
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 136
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 14,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_blade_categories/ahmose_vs_wu_ming.json
===================================================================
--- testcases/emulator_verified/ahmose_blade_categories/ahmose_vs_wu_ming.json prev run
+++ testcases/emulator_verified/ahmose_blade_categories/ahmose_vs_wu_ming.json this run
@@ -0,0 +1,178 @@
+[
+ {
+ "test_id": "ahmose_vs_wu_ming",
+ "description": "C2 battle report 2734693171196841",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 750,
+ "lancer_t6": 100,
+ "marksman_t6": 400
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "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": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Wu Ming": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 800,
+ "lancer_t6": 300,
+ "marksman_t6": 150
+ },
+ "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-22T15:06:27.000Z",
+ "randomseed": 1790089588,
+ "attacker": 508,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171196841",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T15-06-44.108Z-44747d89-70eb-4823-af8a-28057f564319.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1250,
+ "survivors": 508,
+ "losses": 0,
+ "injured": 260,
+ "lightly_injured": 482,
+ "kills": 438
+ },
+ "defender": {
+ "troops": 1250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 438,
+ "lightly_injured": 812,
+ "kills": 260
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 750,
+ "survivors": 8,
+ "losses": 0,
+ "injured": 260,
+ "lightly_injured": 482,
+ "kills": 77
+ },
+ "lancer_t6": {
+ "troops": 100,
+ "survivors": 100,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 96
+ },
+ "marksman_t6": {
+ "troops": 400,
+ "survivors": 400,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 265
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 800,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 280,
+ "lightly_injured": 520,
+ "kills": 67
+ },
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 90
+ },
+ "marksman_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 103
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 16,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_blade_categories/ahmose_wayne_vs_jasser.json
===================================================================
--- testcases/emulator_verified/ahmose_blade_categories/ahmose_wayne_vs_jasser.json prev run
+++ testcases/emulator_verified/ahmose_blade_categories/ahmose_wayne_vs_jasser.json this run
@@ -0,0 +1,191 @@
+[
+ {
+ "test_id": "ahmose_wayne_vs_jasser",
+ "description": "C2 battle report 2734693171197180",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Wayne": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 300,
+ "lancer_t6": 100,
+ "marksman_t6": 250
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 281.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 421.39,
+ "defense": 427.39,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 950,
+ "lancer_t6": 150,
+ "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": 297.4,
+ "defense": 286.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-22T15:09:51.000Z",
+ "randomseed": 1790089792,
+ "attacker": 0,
+ "defender": 278,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171197180",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T15-10-08.100Z-a59787e4-e5da-4bf6-b497-d4927adc11de.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 650,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 228,
+ "lightly_injured": 422,
+ "kills": 394
+ },
+ "defender": {
+ "troops": 1400,
+ "survivors": 278,
+ "losses": 0,
+ "injured": 394,
+ "lightly_injured": 728,
+ "kills": 228
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 40
+ },
+ "lancer_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 62
+ },
+ "marksman_t6": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 292
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 950,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 333,
+ "lightly_injured": 617,
+ "kills": 24
+ },
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 20,
+ "losses": 0,
+ "injured": 46,
+ "lightly_injured": 84,
+ "kills": 56
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 258,
+ "losses": 0,
+ "injured": 15,
+ "lightly_injured": 27,
+ "kills": 148
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Wayne": {
+ "Roundabout Hit": {
+ "triggered": 30,
+ "displayed_kills": 41
+ },
+ "Thunder Strike": {
+ "triggered": 37,
+ "displayed_kills": 29
+ }
+ },
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 8,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_blade_categories/jasser_vs_ahmose_01.json
===================================================================
--- testcases/emulator_verified/ahmose_blade_categories/jasser_vs_ahmose_01.json prev run
+++ testcases/emulator_verified/ahmose_blade_categories/jasser_vs_ahmose_01.json this run
@@ -0,0 +1,176 @@
+[
+ {
+ "test_id": "jasser_vs_ahmose_01",
+ "description": "C2 battle report 2734693171196709",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 950,
+ "lancer_t6": 300,
+ "marksman_t6": 150
+ },
+ "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": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 750,
+ "lancer_t6": 100,
+ "marksman_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "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-22T15:04:59.000Z",
+ "randomseed": 1790089500,
+ "attacker": 0,
+ "defender": 438,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171196709",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T15-05-27.108Z-177fb503-6608-4232-b234-35ad851c8d8b.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 491,
+ "lightly_injured": 909,
+ "kills": 180
+ },
+ "defender": {
+ "troops": 950,
+ "survivors": 438,
+ "losses": 0,
+ "injured": 180,
+ "lightly_injured": 332,
+ "kills": 491
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 950,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 333,
+ "lightly_injured": 617,
+ "kills": 27
+ },
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 65
+ },
+ "marksman_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 88
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 750,
+ "survivors": 238,
+ "losses": 0,
+ "injured": 180,
+ "lightly_injured": 332,
+ "kills": 168
+ },
+ "lancer_t6": {
+ "troops": 100,
+ "survivors": 100,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 132
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 100,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 191
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 13,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_blade_categories/jasser_vs_ahmose_wayne.json
===================================================================
--- testcases/emulator_verified/ahmose_blade_categories/jasser_vs_ahmose_wayne.json prev run
+++ testcases/emulator_verified/ahmose_blade_categories/jasser_vs_ahmose_wayne.json this run
@@ -0,0 +1,191 @@
+[
+ {
+ "test_id": "jasser_vs_ahmose_wayne",
+ "description": "C2 battle report 2734693171197304",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 950,
+ "lancer_t6": 150,
+ "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": 297.4,
+ "defense": 286.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Wayne": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 300,
+ "lancer_t6": 100,
+ "marksman_t6": 250
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 281.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 421.39,
+ "defense": 427.39,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-22T15:11:07.000Z",
+ "randomseed": 1790089868,
+ "attacker": 278,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171197304",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T15-11-35.107Z-5217e120-fe91-45f4-8363-1f6c9245855c.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1400,
+ "survivors": 278,
+ "losses": 0,
+ "injured": 394,
+ "lightly_injured": 728,
+ "kills": 228
+ },
+ "defender": {
+ "troops": 650,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 228,
+ "lightly_injured": 422,
+ "kills": 394
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 950,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 333,
+ "lightly_injured": 617,
+ "kills": 24
+ },
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 20,
+ "losses": 0,
+ "injured": 46,
+ "lightly_injured": 84,
+ "kills": 56
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 258,
+ "losses": 0,
+ "injured": 15,
+ "lightly_injured": 27,
+ "kills": 148
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 40
+ },
+ "lancer_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 62
+ },
+ "marksman_t6": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 292
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {
+ "Wayne": {
+ "Roundabout Hit": {
+ "triggered": 30,
+ "displayed_kills": 41
+ },
+ "Thunder Strike": {
+ "triggered": 37,
+ "displayed_kills": 29
+ }
+ },
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 8,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_blade_categories/wu_ming_vs_ahmose.json
===================================================================
--- testcases/emulator_verified/ahmose_blade_categories/wu_ming_vs_ahmose.json prev run
+++ testcases/emulator_verified/ahmose_blade_categories/wu_ming_vs_ahmose.json this run
@@ -0,0 +1,160 @@
+[
+ {
+ "test_id": "wu_ming_vs_ahmose",
+ "description": "C2 battle report 2734693171196993",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Wu Ming": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 950,
+ "marksman_t6": 300
+ },
+ "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": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 1000,
+ "marksman_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "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-22T15:08:10.000Z",
+ "randomseed": 1790089691,
+ "attacker": 538,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171196993",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T15-08-38.109Z-694b6315-6393-46a5-864a-5d4c442c7458.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1250,
+ "survivors": 538,
+ "losses": 0,
+ "injured": 250,
+ "lightly_injured": 462,
+ "kills": 385
+ },
+ "defender": {
+ "troops": 1100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 385,
+ "lightly_injured": 715,
+ "kills": 250
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 950,
+ "survivors": 238,
+ "losses": 0,
+ "injured": 250,
+ "lightly_injured": 462,
+ "kills": 137
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 300,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 248
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 113
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 137
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 26,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_bucket_interactions/ahmose_hendrik_vs_jasser.json
===================================================================
--- testcases/emulator_verified/ahmose_bucket_interactions/ahmose_hendrik_vs_jasser.json prev run
+++ testcases/emulator_verified/ahmose_bucket_interactions/ahmose_hendrik_vs_jasser.json this run
@@ -0,0 +1,160 @@
+[
+ {
+ "test_id": "ahmose_hendrik_vs_jasser",
+ "description": "C2 battle report 2734693171218664",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Hendrik": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 400
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 281.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 444.21,
+ "defense": 450.21,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 200,
+ "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": 297.4,
+ "defense": 286.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-22T18:22:40.000Z",
+ "randomseed": 1790101361,
+ "attacker": 23,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171218664",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T18-22-57.101Z-5b529683-511e-4294-905a-3e51e4794114.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 400,
+ "survivors": 23,
+ "losses": 0,
+ "injured": 132,
+ "lightly_injured": 245,
+ "kills": 175
+ },
+ "defender": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 132
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 400,
+ "survivors": 23,
+ "losses": 0,
+ "injured": 132,
+ "lightly_injured": 245,
+ "kills": 175
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 7
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 125
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 25,
+ "displayed_kills": null
+ }
+ },
+ "Hendrik": {
+ "Armor of Barnacles": {
+ "triggered": 31,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_bucket_interactions/ahmose_jasser_vs_jasser.json
===================================================================
--- testcases/emulator_verified/ahmose_bucket_interactions/ahmose_jasser_vs_jasser.json prev run
+++ testcases/emulator_verified/ahmose_bucket_interactions/ahmose_jasser_vs_jasser.json this run
@@ -0,0 +1,144 @@
+[
+ {
+ "test_id": "ahmose_jasser_vs_jasser",
+ "description": "C2 battle report 2734693171218409",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Jasser": {
+ "skill_1": 4,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 800
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 281.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 354.37,
+ "defense": 360.37,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 950
+ },
+ "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-22T18:21:21.000Z",
+ "randomseed": 1790101282,
+ "attacker": 649,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171218409",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T18-21-38.098Z-b4fbf9d1-bb4e-4447-9a20-a9399f385138.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 800,
+ "survivors": 649,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 98,
+ "kills": 333
+ },
+ "defender": {
+ "troops": 950,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 333,
+ "lightly_injured": 617,
+ "kills": 53
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 800,
+ "survivors": 649,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 98,
+ "kills": 333
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 950,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 333,
+ "lightly_injured": 617,
+ "kills": 53
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 25,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_bucket_interactions/ahmose_reina_vs_jasser.json
===================================================================
--- testcases/emulator_verified/ahmose_bucket_interactions/ahmose_reina_vs_jasser.json prev run
+++ testcases/emulator_verified/ahmose_bucket_interactions/ahmose_reina_vs_jasser.json this run
@@ -0,0 +1,397 @@
+[
+ {
+ "test_id": "ahmose_reina_vs_jasser",
+ "description": "C2 battle report 2734693171219563",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Reina": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 250
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 364.28,
+ "defense": 368.53,
+ "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": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_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": 297.4,
+ "defense": 286.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-22T18:28:49.000Z",
+ "randomseed": 1790101730,
+ "attacker": 92,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171219563",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T18-29-06.104Z-bc3b073a-db1a-47ab-adc2-aabf264a1b99.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 250,
+ "survivors": 92,
+ "losses": 0,
+ "injured": 56,
+ "lightly_injured": 102,
+ "kills": 210
+ },
+ "defender": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 56
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 250,
+ "survivors": 92,
+ "losses": 0,
+ "injured": 56,
+ "lightly_injured": 102,
+ "kills": 210
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 56
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 66,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-22T18:29:34.000Z",
+ "randomseed": 1790101775,
+ "attacker": 101,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171219676",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T18-29-51.095Z-caba2cae-6f33-4cbf-abe8-ad9ded73fddf.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 250,
+ "survivors": 101,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 96,
+ "kills": 210
+ },
+ "defender": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 53
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 250,
+ "survivors": 101,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 96,
+ "kills": 210
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 53
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 65,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-22T18:30:19.000Z",
+ "randomseed": 1790101820,
+ "attacker": 95,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171219858",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T18-30-36.096Z-07a2c638-c1cf-44b3-8873-a80398a2dcb4.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 250,
+ "survivors": 95,
+ "losses": 0,
+ "injured": 55,
+ "lightly_injured": 100,
+ "kills": 210
+ },
+ "defender": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 55
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 250,
+ "survivors": 95,
+ "losses": 0,
+ "injured": 55,
+ "lightly_injured": 100,
+ "kills": 210
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 55
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 66,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-22T18:31:04.000Z",
+ "randomseed": 1790101865,
+ "attacker": 99,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171219894",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T18-31-21.099Z-c095dc57-96aa-4f1e-ae83-15f618e82c9c.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 250,
+ "survivors": 99,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 98,
+ "kills": 210
+ },
+ "defender": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 53
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 250,
+ "survivors": 99,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 98,
+ "kills": 210
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 53
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 65,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-22T18:31:49.000Z",
+ "randomseed": 1790101910,
+ "attacker": 89,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171219929",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T18-32-06.096Z-b125850c-1b83-4ff7-a940-cfbc1aed6e0b.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 250,
+ "survivors": 89,
+ "losses": 0,
+ "injured": 57,
+ "lightly_injured": 104,
+ "kills": 210
+ },
+ "defender": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 57
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 250,
+ "survivors": 89,
+ "losses": 0,
+ "injured": 57,
+ "lightly_injured": 104,
+ "kills": 210
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 57
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 67,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_bucket_interactions/ahmose_seo_yoon_vs_jasser.json
===================================================================
--- testcases/emulator_verified/ahmose_bucket_interactions/ahmose_seo_yoon_vs_jasser.json prev run
+++ testcases/emulator_verified/ahmose_bucket_interactions/ahmose_seo_yoon_vs_jasser.json this run
@@ -0,0 +1,144 @@
+[
+ {
+ "test_id": "ahmose_seo_yoon_vs_jasser",
+ "description": "C2 battle report 2734693171218011",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Seo-yoon": {
+ "skill_1": 4,
+ "skill_2": 3
+ }
+ },
+ "troops": {
+ "infantry_t6": 800
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 281.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 354.37,
+ "defense": 360.37,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 950
+ },
+ "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-22T18:19:04.000Z",
+ "randomseed": 1790101145,
+ "attacker": 649,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171218011",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T18-19-21.104Z-1f3b53e8-b674-4271-b4ce-f3f76cb10fb7.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 800,
+ "survivors": 649,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 98,
+ "kills": 333
+ },
+ "defender": {
+ "troops": 950,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 333,
+ "lightly_injured": 617,
+ "kills": 53
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 800,
+ "survivors": 649,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 98,
+ "kills": 333
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 950,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 333,
+ "lightly_injured": 617,
+ "kills": 53
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 25,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_bucket_interactions/ahmose_vs_gatot.json
===================================================================
--- testcases/emulator_verified/ahmose_bucket_interactions/ahmose_vs_gatot.json prev run
+++ testcases/emulator_verified/ahmose_bucket_interactions/ahmose_vs_gatot.json this run
@@ -0,0 +1,158 @@
+[
+ {
+ "test_id": "ahmose_vs_gatot",
+ "description": "C2 battle report 2734693171218916",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "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": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gatot": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 50,
+ "marksman_t6": 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-22T18:24:10.000Z",
+ "randomseed": 1790101451,
+ "attacker": 698,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171218916",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T18-24-27.118Z-7d42dc50-f6ac-49b2-930d-fd68c445b6f0.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 698,
+ "losses": 0,
+ "injured": 106,
+ "lightly_injured": 196,
+ "kills": 123
+ },
+ "defender": {
+ "troops": 350,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 123,
+ "lightly_injured": 227,
+ "kills": 106
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 698,
+ "losses": 0,
+ "injured": 106,
+ "lightly_injured": 196,
+ "kills": 123
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 16
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 90
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 22,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": 93,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_bucket_interactions/ahmose_vs_wu_ming.json
===================================================================
--- testcases/emulator_verified/ahmose_bucket_interactions/ahmose_vs_wu_ming.json prev run
+++ testcases/emulator_verified/ahmose_bucket_interactions/ahmose_vs_wu_ming.json this run
@@ -0,0 +1,160 @@
+[
+ {
+ "test_id": "ahmose_vs_wu_ming",
+ "description": "C2 battle report 2734693171220225",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 800,
+ "marksman_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "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": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Wu Ming": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 600,
+ "marksman_t6": 300
+ },
+ "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-22T18:36:21.000Z",
+ "randomseed": 1790102182,
+ "attacker": 0,
+ "defender": 72,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171220225",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T18-36-38.097Z-ab3e31af-03a2-4f46-b002-02d8bc8e5c64.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 900,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 315,
+ "lightly_injured": 585,
+ "kills": 290
+ },
+ "defender": {
+ "troops": 900,
+ "survivors": 72,
+ "losses": 0,
+ "injured": 290,
+ "lightly_injured": 538,
+ "kills": 315
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 800,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 280,
+ "lightly_injured": 520,
+ "kills": 96
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 194
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 78
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 72,
+ "losses": 0,
+ "injured": 80,
+ "lightly_injured": 148,
+ "kills": 237
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 26,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_bucket_interactions/wu_ming_vs_ahmose.json
===================================================================
--- testcases/emulator_verified/ahmose_bucket_interactions/wu_ming_vs_ahmose.json prev run
+++ testcases/emulator_verified/ahmose_bucket_interactions/wu_ming_vs_ahmose.json this run
@@ -0,0 +1,151 @@
+[
+ {
+ "test_id": "wu_ming_vs_ahmose",
+ "description": "C2 battle report 2734693171220596",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Wu Ming": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 400,
+ "marksman_t6": 200
+ },
+ "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": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "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-22T18:42:46.000Z",
+ "randomseed": 1790102567,
+ "attacker": 0,
+ "defender": 200,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171220596",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T18-43-14.097Z-265e8ca3-c43f-4d36-abf1-87639a7daf66.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 280
+ },
+ "defender": {
+ "troops": 1000,
+ "survivors": 200,
+ "losses": 0,
+ "injured": 280,
+ "lightly_injured": 520,
+ "kills": 210
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 77
+ },
+ "marksman_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 203
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 200,
+ "losses": 0,
+ "injured": 280,
+ "lightly_injured": 520,
+ "kills": 210
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 40,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_c2_discriminators/ahmose_renee_vs_jasser_01.json
===================================================================
--- testcases/emulator_verified/ahmose_c2_discriminators/ahmose_renee_vs_jasser_01.json prev run
+++ testcases/emulator_verified/ahmose_c2_discriminators/ahmose_renee_vs_jasser_01.json this run
@@ -0,0 +1,178 @@
+[
+ {
+ "test_id": "ahmose_renee_vs_jasser_01",
+ "description": "C2 battle report 2734693171177035",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ },
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 60,
+ "lancer_t6": 650,
+ "marksman_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 345.57,
+ "defense": 338.12,
+ "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": {
+ "Jasser": {
+ "skill_1": 4,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 700,
+ "marksman_t6": 450
+ },
+ "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": 354.37,
+ "defense": 360.37,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-22T12:45:14.000Z",
+ "randomseed": 1790081115,
+ "attacker": 0,
+ "defender": 366,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171177035",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T12-45-42.099Z-de3bbdf9-4bcd-49fa-87a0-3c3db458e46c.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1010,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 354,
+ "lightly_injured": 656,
+ "kills": 275
+ },
+ "defender": {
+ "troops": 1150,
+ "survivors": 366,
+ "losses": 0,
+ "injured": 275,
+ "lightly_injured": 509,
+ "kills": 354
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 60,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 21,
+ "lightly_injured": 39,
+ "kills": 0
+ },
+ "lancer_t6": {
+ "troops": 650,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 228,
+ "lightly_injured": 422,
+ "kills": 167
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 108
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 700,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 245,
+ "lightly_injured": 455,
+ "kills": 38
+ },
+ "marksman_t6": {
+ "troops": 450,
+ "survivors": 366,
+ "losses": 0,
+ "injured": 30,
+ "lightly_injured": 54,
+ "kills": 316
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 1,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 16,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_c2_discriminators/ahmose_renee_vs_jasser_02.json
===================================================================
--- testcases/emulator_verified/ahmose_c2_discriminators/ahmose_renee_vs_jasser_02.json prev run
+++ testcases/emulator_verified/ahmose_c2_discriminators/ahmose_renee_vs_jasser_02.json this run
@@ -0,0 +1,188 @@
+[
+ {
+ "test_id": "ahmose_renee_vs_jasser_02",
+ "description": "C2 battle report 2734693171177020",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 20,
+ "lancer_t6": 150,
+ "marksman_t6": 200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "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": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 300,
+ "lancer_t6": 300,
+ "marksman_t6": 150
+ },
+ "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-22T12:45:00.000Z",
+ "randomseed": 1790081101,
+ "attacker": 0,
+ "defender": 227,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171177020",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T12-45-17.101Z-18bff28d-9e22-4981-a0e1-62e0c4fabf17.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 370,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 130,
+ "lightly_injured": 240,
+ "kills": 184
+ },
+ "defender": {
+ "troops": 750,
+ "survivors": 227,
+ "losses": 0,
+ "injured": 184,
+ "lightly_injured": 339,
+ "kills": 130
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 20,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 7,
+ "lightly_injured": 13,
+ "kills": 0
+ },
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 64
+ },
+ "marksman_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 120
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 5
+ },
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 77,
+ "losses": 0,
+ "injured": 79,
+ "lightly_injured": 144,
+ "kills": 53
+ },
+ "marksman_t6": {
+ "troops": 150,
+ "survivors": 150,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 72
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 1,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 15,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_c2_discriminators/ahmose_vs_jasser_02.json
===================================================================
--- testcases/emulator_verified/ahmose_c2_discriminators/ahmose_vs_jasser_02.json prev run
+++ testcases/emulator_verified/ahmose_c2_discriminators/ahmose_vs_jasser_02.json this run
@@ -0,0 +1,166 @@
+[
+ {
+ "test_id": "ahmose_vs_jasser_02",
+ "description": "C2 battle report 2734693171177957",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 320,
+ "lancer_t6": 350,
+ "marksman_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 345.57,
+ "defense": 338.12,
+ "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": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 4,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 300,
+ "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": 354.37,
+ "defense": 360.37,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-22T12:55:54.000Z",
+ "randomseed": 1790081755,
+ "attacker": 0,
+ "defender": 54,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171177957",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T12-56-22.104Z-84298a33-fbb1-4b2d-b18d-32141cb25279.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 970,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 340,
+ "lightly_injured": 630,
+ "kills": 297
+ },
+ "defender": {
+ "troops": 900,
+ "survivors": 54,
+ "losses": 0,
+ "injured": 297,
+ "lightly_injured": 549,
+ "kills": 340
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 320,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 112,
+ "lightly_injured": 208,
+ "kills": 15
+ },
+ "lancer_t6": {
+ "troops": 350,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 123,
+ "lightly_injured": 227,
+ "kills": 90
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 192
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 15
+ },
+ "lancer_t6": {
+ "troops": 600,
+ "survivors": 54,
+ "losses": 0,
+ "injured": 192,
+ "lightly_injured": 354,
+ "kills": 325
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 7,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_c2_discriminators/ahmose_vs_jasser_03.json
===================================================================
--- testcases/emulator_verified/ahmose_c2_discriminators/ahmose_vs_jasser_03.json prev run
+++ testcases/emulator_verified/ahmose_c2_discriminators/ahmose_vs_jasser_03.json this run
@@ -0,0 +1,176 @@
+[
+ {
+ "test_id": "ahmose_vs_jasser_03",
+ "description": "C2 battle report 2734693171177931",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 30,
+ "lancer_t6": 350,
+ "marksman_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "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": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 300,
+ "lancer_t6": 150,
+ "marksman_t6": 150
+ },
+ "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-22T12:55:40.000Z",
+ "randomseed": 1790081741,
+ "attacker": 0,
+ "defender": 117,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171177931",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T12-55-57.105Z-ff77fd1d-cc80-4c0a-9f4e-0d5f7089a607.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 680,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 239,
+ "lightly_injured": 441,
+ "kills": 170
+ },
+ "defender": {
+ "troops": 600,
+ "survivors": 117,
+ "losses": 0,
+ "injured": 170,
+ "lightly_injured": 313,
+ "kills": 239
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 30,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 11,
+ "lightly_injured": 19,
+ "kills": 1
+ },
+ "lancer_t6": {
+ "troops": 350,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 123,
+ "lightly_injured": 227,
+ "kills": 36
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 133
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 16
+ },
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 72
+ },
+ "marksman_t6": {
+ "troops": 150,
+ "survivors": 117,
+ "losses": 0,
+ "injured": 12,
+ "lightly_injured": 21,
+ "kills": 151
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 1,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_c2_discriminators/ahmose_vs_sonya_01.json
===================================================================
--- testcases/emulator_verified/ahmose_c2_discriminators/ahmose_vs_sonya_01.json prev run
+++ testcases/emulator_verified/ahmose_c2_discriminators/ahmose_vs_sonya_01.json this run
@@ -0,0 +1,188 @@
+[
+ {
+ "test_id": "ahmose_vs_sonya_01",
+ "description": "C2 battle report 2734693171176841",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 500,
+ "lancer_t6": 150,
+ "marksman_t6": 450
+ },
+ "stats": {
+ "infantry": {
+ "attack": 345.57,
+ "defense": 338.12,
+ "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": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Sonya": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 150,
+ "lancer_t6": 250,
+ "marksman_t6": 200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 529.96,
+ "defense": 534.21,
+ "lethality": 323.16,
+ "health": 285.53
+ },
+ "marksman": {
+ "attack": 283.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-22T12:42:45.000Z",
+ "randomseed": 1790080966,
+ "attacker": 367,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171176841",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T12-43-13.106Z-fc4468ad-1626-4ce4-a2db-c796c024455b.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1100,
+ "survivors": 367,
+ "losses": 0,
+ "injured": 258,
+ "lightly_injured": 475,
+ "kills": 211
+ },
+ "defender": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 211,
+ "lightly_injured": 389,
+ "kills": 258
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 18
+ },
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 41
+ },
+ "marksman_t6": {
+ "troops": 450,
+ "survivors": 367,
+ "losses": 0,
+ "injured": 30,
+ "lightly_injured": 53,
+ "kills": 152
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 4
+ },
+ "lancer_t6": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 116
+ },
+ "marksman_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 138
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 7,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 19,
+ "displayed_kills": 7
+ },
+ "Torrential Impact": {
+ "triggered": 7,
+ "displayed_kills": 9
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_c2_discriminators/ahmose_vs_sonya_02.json
===================================================================
--- testcases/emulator_verified/ahmose_c2_discriminators/ahmose_vs_sonya_02.json prev run
+++ testcases/emulator_verified/ahmose_c2_discriminators/ahmose_vs_sonya_02.json this run
@@ -0,0 +1,184 @@
+[
+ {
+ "test_id": "ahmose_vs_sonya_02",
+ "description": "C2 battle report 2734693171176800",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 100,
+ "lancer_t6": 650,
+ "marksman_t6": 200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "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": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Sonya": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 150,
+ "lancer_t6": 700,
+ "marksman_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 369.5,
+ "defense": 368.8,
+ "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-22T12:42:31.000Z",
+ "randomseed": 1790080952,
+ "attacker": 0,
+ "defender": 141,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171176800",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T12-42-48.098Z-15a8c01d-faeb-4cc4-ad14-44de148e5a3d.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 950,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 333,
+ "lightly_injured": 617,
+ "kills": 354
+ },
+ "defender": {
+ "troops": 1150,
+ "survivors": 141,
+ "losses": 0,
+ "injured": 354,
+ "lightly_injured": 655,
+ "kills": 333
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 6
+ },
+ "lancer_t6": {
+ "troops": 650,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 228,
+ "lightly_injured": 422,
+ "kills": 137
+ },
+ "marksman_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 211
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 1
+ },
+ "lancer_t6": {
+ "troops": 700,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 245,
+ "lightly_injured": 455,
+ "kills": 147
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 141,
+ "losses": 0,
+ "injured": 56,
+ "lightly_injured": 103,
+ "kills": 185
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 2,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 15,
+ "displayed_kills": 10
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_02.json
===================================================================
--- testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_02.json prev run
+++ testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_02.json this run
@@ -0,0 +1,157 @@
+[
+ {
+ "test_id": "jasser_vs_ahmose_02",
+ "description": "C2 battle report 2734693171177279",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 4,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 900
+ },
+ "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": 354.37,
+ "defense": 360.37,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 160,
+ "lancer_t6": 80,
+ "marksman_t6": 600
+ },
+ "stats": {
+ "infantry": {
+ "attack": 345.57,
+ "defense": 338.12,
+ "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-22T12:48:07.000Z",
+ "randomseed": 1790081288,
+ "attacker": 0,
+ "defender": 148,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171177279",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T12-48-24.097Z-fe44dfcc-6d2b-41be-8307-7954dfa4fae4.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 900,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 315,
+ "lightly_injured": 585,
+ "kills": 243
+ },
+ "defender": {
+ "troops": 840,
+ "survivors": 148,
+ "losses": 0,
+ "injured": 243,
+ "lightly_injured": 449,
+ "kills": 315
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 900,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 315,
+ "lightly_injured": 585,
+ "kills": 243
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 160,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 56,
+ "lightly_injured": 104,
+ "kills": 13
+ },
+ "lancer_t6": {
+ "troops": 80,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 28,
+ "lightly_injured": 52,
+ "kills": 32
+ },
+ "marksman_t6": {
+ "troops": 600,
+ "survivors": 148,
+ "losses": 0,
+ "injured": 159,
+ "lightly_injured": 293,
+ "kills": 270
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 11,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_03.json
===================================================================
--- testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_03.json prev run
+++ testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_03.json this run
@@ -0,0 +1,158 @@
+[
+ {
+ "test_id": "jasser_vs_ahmose_03",
+ "description": "C2 battle report 2734693171177298",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 900
+ },
+ "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": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 20,
+ "lancer_t6": 350,
+ "marksman_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "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-22T12:48:21.000Z",
+ "randomseed": 1790081302,
+ "attacker": 35,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171177298",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T12-48-49.096Z-fbc5d85f-5dec-46f2-8056-c4c7aee685fc.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 900,
+ "survivors": 35,
+ "losses": 0,
+ "injured": 303,
+ "lightly_injured": 562,
+ "kills": 165
+ },
+ "defender": {
+ "troops": 470,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 165,
+ "lightly_injured": 305,
+ "kills": 303
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 900,
+ "survivors": 35,
+ "losses": 0,
+ "injured": 303,
+ "lightly_injured": 562,
+ "kills": 165
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 20,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 7,
+ "lightly_injured": 13,
+ "kills": 3
+ },
+ "lancer_t6": {
+ "troops": 350,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 123,
+ "lightly_injured": 227,
+ "kills": 119
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 181
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 3,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_04.json
===================================================================
--- testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_04.json prev run
+++ testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_04.json this run
@@ -0,0 +1,157 @@
+[
+ {
+ "test_id": "jasser_vs_ahmose_04",
+ "description": "C2 battle report 2734693171177719",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 4,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_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": 354.37,
+ "defense": 360.37,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 130,
+ "lancer_t6": 150,
+ "marksman_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 345.57,
+ "defense": 338.12,
+ "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-22T12:52:46.000Z",
+ "randomseed": 1790081567,
+ "attacker": 0,
+ "defender": 377,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171177719",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T12-53-03.101Z-337f0e8b-d52c-4e1b-8e59-84b2c53e8a9e.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 72
+ },
+ "defender": {
+ "troops": 580,
+ "survivors": 377,
+ "losses": 0,
+ "injured": 72,
+ "lightly_injured": 131,
+ "kills": 210
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 72
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 130,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 46,
+ "lightly_injured": 84,
+ "kills": 14
+ },
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 77,
+ "losses": 0,
+ "injured": 26,
+ "lightly_injured": 47,
+ "kills": 61
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 300,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 135
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 14,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_05.json
===================================================================
--- testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_05.json prev run
+++ testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_05.json this run
@@ -0,0 +1,158 @@
+[
+ {
+ "test_id": "jasser_vs_ahmose_05",
+ "description": "C2 battle report 2734693171177754",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 900
+ },
+ "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": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 30,
+ "lancer_t6": 500,
+ "marksman_t6": 200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "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-22T12:53:00.000Z",
+ "randomseed": 1790081581,
+ "attacker": 0,
+ "defender": 526,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171177754",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T12-53-28.100Z-3e2a12a6-6e6e-4dc7-ae84-7be9834d769f.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 900,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 315,
+ "lightly_injured": 585,
+ "kills": 72
+ },
+ "defender": {
+ "troops": 730,
+ "survivors": 526,
+ "losses": 0,
+ "injured": 72,
+ "lightly_injured": 132,
+ "kills": 315
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 900,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 315,
+ "lightly_injured": 585,
+ "kills": 72
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 30,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 11,
+ "lightly_injured": 19,
+ "kills": 7
+ },
+ "lancer_t6": {
+ "troops": 500,
+ "survivors": 326,
+ "losses": 0,
+ "injured": 61,
+ "lightly_injured": 113,
+ "kills": 152
+ },
+ "marksman_t6": {
+ "troops": 200,
+ "survivors": 200,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 156
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 4,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_renee_01.json
===================================================================
--- testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_renee_01.json prev run
+++ testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_renee_01.json this run
@@ -0,0 +1,178 @@
+[
+ {
+ "test_id": "jasser_vs_ahmose_renee_01",
+ "description": "C2 battle report 2734693171177561",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 4,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 500,
+ "lancer_t6": 300,
+ "marksman_t6": 450
+ },
+ "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": 354.37,
+ "defense": 360.37,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ },
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 500,
+ "lancer_t6": 350
+ },
+ "stats": {
+ "infantry": {
+ "attack": 345.57,
+ "defense": 338.12,
+ "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": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-22T12:51:05.000Z",
+ "randomseed": 1790081466,
+ "attacker": 0,
+ "defender": 11,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171177561",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T12-51-22.103Z-fe039a6d-4ca4-4470-9541-9029e88157a9.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 438,
+ "lightly_injured": 812,
+ "kills": 294
+ },
+ "defender": {
+ "troops": 850,
+ "survivors": 11,
+ "losses": 0,
+ "injured": 294,
+ "lightly_injured": 545,
+ "kills": 438
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 17
+ },
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 62
+ },
+ "marksman_t6": {
+ "troops": 450,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 158,
+ "lightly_injured": 292,
+ "kills": 215
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 19
+ },
+ "lancer_t6": {
+ "troops": 350,
+ "survivors": 11,
+ "losses": 0,
+ "injured": 119,
+ "lightly_injured": 220,
+ "kills": 419
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 5,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 22,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_renee_02.json
===================================================================
--- testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_renee_02.json prev run
+++ testcases/emulator_verified/ahmose_c2_discriminators/jasser_vs_ahmose_renee_02.json this run
@@ -0,0 +1,179 @@
+[
+ {
+ "test_id": "jasser_vs_ahmose_renee_02",
+ "description": "C2 battle report 2734693171177580",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 500,
+ "marksman_t6": 650
+ },
+ "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": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 100,
+ "lancer_t6": 150,
+ "marksman_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "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": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-22T12:51:19.000Z",
+ "randomseed": 1790081480,
+ "attacker": 224,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171177580",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T12-51-47.104Z-def3d98f-8305-41c6-8f29-79e7b22ec82a.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1150,
+ "survivors": 224,
+ "losses": 0,
+ "injured": 325,
+ "lightly_injured": 601,
+ "kills": 123
+ },
+ "defender": {
+ "troops": 350,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 123,
+ "lightly_injured": 227,
+ "kills": 325
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 6
+ },
+ "marksman_t6": {
+ "troops": 650,
+ "survivors": 224,
+ "losses": 0,
+ "injured": 150,
+ "lightly_injured": 276,
+ "kills": 117
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 10
+ },
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 179
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 136
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 4,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 21,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_01.json
===================================================================
--- testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_01.json prev run
+++ testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_01.json this run
@@ -0,0 +1,188 @@
+[
+ {
+ "test_id": "sonya_vs_ahmose_01",
+ "description": "C2 battle report 2734693171177424",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Sonya": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 80,
+ "lancer_t6": 400,
+ "marksman_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 529.96,
+ "defense": 534.21,
+ "lethality": 323.16,
+ "health": 285.53
+ },
+ "marksman": {
+ "attack": 283.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 400,
+ "lancer_t6": 650,
+ "marksman_t6": 200
+ },
+ "stats": {
+ "infantry": {
+ "attack": 345.57,
+ "defense": 338.12,
+ "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-22T12:49:41.000Z",
+ "randomseed": 1790081382,
+ "attacker": 0,
+ "defender": 125,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171177424",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T12-49-58.101Z-fe87620a-3310-42f9-83c9-51edb37e6018.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 580,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 203,
+ "lightly_injured": 377,
+ "kills": 395
+ },
+ "defender": {
+ "troops": 1250,
+ "survivors": 125,
+ "losses": 0,
+ "injured": 395,
+ "lightly_injured": 730,
+ "kills": 203
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 80,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 28,
+ "lightly_injured": 52,
+ "kills": 2
+ },
+ "lancer_t6": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 237
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 156
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 12
+ },
+ "lancer_t6": {
+ "troops": 650,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 228,
+ "lightly_injured": 422,
+ "kills": 80
+ },
+ "marksman_t6": {
+ "troops": 200,
+ "survivors": 125,
+ "losses": 0,
+ "injured": 27,
+ "lightly_injured": 48,
+ "kills": 111
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 22,
+ "displayed_kills": 14
+ },
+ "Torrential Impact": {
+ "triggered": 9,
+ "displayed_kills": 20
+ }
+ }
+ },
+ "defender": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 5,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_02.json
===================================================================
--- testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_02.json prev run
+++ testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_02.json this run
@@ -0,0 +1,166 @@
+[
+ {
+ "test_id": "sonya_vs_ahmose_02",
+ "description": "C2 battle report 2734693171177460",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Sonya": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 400
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 369.5,
+ "defense": 368.8,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 12,
+ "lancer_t6": 80,
+ "marksman_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "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-22T12:49:55.000Z",
+ "randomseed": 1790081396,
+ "attacker": 0,
+ "defender": 148,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171177460",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T12-50-23.096Z-b7da47c0-3c38-4710-b51d-6d8667f2b36a.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 87
+ },
+ "defender": {
+ "troops": 392,
+ "survivors": 148,
+ "losses": 0,
+ "injured": 87,
+ "lightly_injured": 157,
+ "kills": 140
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 87
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 12,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 5,
+ "lightly_injured": 7,
+ "kills": 1
+ },
+ "lancer_t6": {
+ "troops": 80,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 28,
+ "lightly_injured": 52,
+ "kills": 16
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 148,
+ "losses": 0,
+ "injured": 54,
+ "lightly_injured": 98,
+ "kills": 123
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 14,
+ "displayed_kills": 4
+ }
+ }
+ },
+ "defender": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 1,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_03.json
===================================================================
--- testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_03.json prev run
+++ testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_03.json this run
@@ -0,0 +1,170 @@
+[
+ {
+ "test_id": "sonya_vs_ahmose_03",
+ "description": "C2 battle report 2734693171177838",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Sonya": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 30,
+ "lancer_t6": 150,
+ "marksman_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 299.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 529.96,
+ "defense": 534.21,
+ "lethality": 323.16,
+ "health": 285.53
+ },
+ "marksman": {
+ "attack": 283.42,
+ "defense": 289.42,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 950
+ },
+ "stats": {
+ "infantry": {
+ "attack": 345.57,
+ "defense": 338.12,
+ "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-22T12:54:10.000Z",
+ "randomseed": 1790081651,
+ "attacker": 265,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171177838",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T12-54-27.099Z-8da60204-2311-4dfe-bfc9-71d3c4939aee.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 480,
+ "survivors": 265,
+ "losses": 0,
+ "injured": 77,
+ "lightly_injured": 138,
+ "kills": 333
+ },
+ "defender": {
+ "troops": 950,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 333,
+ "lightly_injured": 617,
+ "kills": 77
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 30,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 11,
+ "lightly_injured": 19,
+ "kills": 1
+ },
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 131
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 265,
+ "losses": 0,
+ "injured": 13,
+ "lightly_injured": 22,
+ "kills": 201
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 950,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 333,
+ "lightly_injured": 617,
+ "kills": 77
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 41,
+ "displayed_kills": 6
+ },
+ "Torrential Impact": {
+ "triggered": 16,
+ "displayed_kills": 10
+ }
+ }
+ },
+ "defender": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 20,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_04.json
===================================================================
--- testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_04.json prev run
+++ testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_04.json this run
@@ -0,0 +1,252 @@
+[
+ {
+ "test_id": "sonya_vs_ahmose_04",
+ "description": "C2 battle report 2734693171177863",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Sonya": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 600,
+ "lancer_t6": 250,
+ "marksman_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 369.5,
+ "defense": 368.8,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 950
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "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-22T12:54:24.000Z",
+ "randomseed": 1790081665,
+ "attacker": 147,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171177863",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T12-54-52.100Z-5db46442-8faa-4a7f-826c-f45cdb88de3d.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 950,
+ "survivors": 147,
+ "losses": 0,
+ "injured": 282,
+ "lightly_injured": 521,
+ "kills": 333
+ },
+ "defender": {
+ "troops": 950,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 333,
+ "lightly_injured": 617,
+ "kills": 282
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 35
+ },
+ "lancer_t6": {
+ "troops": 250,
+ "survivors": 47,
+ "losses": 0,
+ "injured": 72,
+ "lightly_injured": 131,
+ "kills": 182
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 100,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 116
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 950,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 333,
+ "lightly_injured": 617,
+ "kills": 282
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 67,
+ "displayed_kills": 14
+ }
+ }
+ },
+ "defender": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 27,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-22T12:57:52.000Z",
+ "randomseed": 1790081873,
+ "attacker": 147,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171178088",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T12-58-20.101Z-a73e8bc8-9d90-4523-8acb-80c29e136dc2.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 950,
+ "survivors": 147,
+ "losses": 0,
+ "injured": 282,
+ "lightly_injured": 521,
+ "kills": 333
+ },
+ "defender": {
+ "troops": 950,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 333,
+ "lightly_injured": 617,
+ "kills": 282
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 35
+ },
+ "lancer_t6": {
+ "troops": 250,
+ "survivors": 47,
+ "losses": 0,
+ "injured": 72,
+ "lightly_injured": 131,
+ "kills": 182
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 100,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 116
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 950,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 333,
+ "lightly_injured": 617,
+ "kills": 282
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 67,
+ "displayed_kills": 14
+ }
+ }
+ },
+ "defender": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 27,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_05.json
===================================================================
--- testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_05.json prev run
+++ testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_05.json this run
@@ -0,0 +1,166 @@
+[
+ {
+ "test_id": "sonya_vs_ahmose_05",
+ "description": "C2 battle report 2734693171178226",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Sonya": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 600,
+ "lancer_t6": 250,
+ "marksman_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 369.5,
+ "defense": 368.8,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 900
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "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-22T12:59:23.000Z",
+ "randomseed": 1790081964,
+ "attacker": 323,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171178226",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T12-59-51.099Z-5306b071-25d2-4a37-bafa-051232d24bcb.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 950,
+ "survivors": 323,
+ "losses": 0,
+ "injured": 220,
+ "lightly_injured": 407,
+ "kills": 315
+ },
+ "defender": {
+ "troops": 900,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 315,
+ "lightly_injured": 585,
+ "kills": 220
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 34
+ },
+ "lancer_t6": {
+ "troops": 250,
+ "survivors": 223,
+ "losses": 0,
+ "injured": 10,
+ "lightly_injured": 17,
+ "kills": 173
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 100,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 108
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 900,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 315,
+ "lightly_injured": 585,
+ "kills": 220
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 61,
+ "displayed_kills": 8
+ }
+ }
+ },
+ "defender": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 24,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_06.json
===================================================================
--- testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_06.json prev run
+++ testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_06.json this run
@@ -0,0 +1,166 @@
+[
+ {
+ "test_id": "sonya_vs_ahmose_06",
+ "description": "C2 battle report 2734693171178339",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Sonya": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 600,
+ "lancer_t6": 250,
+ "marksman_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 369.5,
+ "defense": 368.8,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "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-22T13:00:45.000Z",
+ "randomseed": 1790082046,
+ "attacker": 0,
+ "defender": 130,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171178339",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T13-01-13.104Z-d4d26eb2-c4e5-4af7-bc00-3136cdfaa568.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 950,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 333,
+ "lightly_injured": 617,
+ "kills": 305
+ },
+ "defender": {
+ "troops": 1000,
+ "survivors": 130,
+ "losses": 0,
+ "injured": 305,
+ "lightly_injured": 565,
+ "kills": 333
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 30
+ },
+ "lancer_t6": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 162
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 113
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 130,
+ "losses": 0,
+ "injured": 305,
+ "lightly_injured": 565,
+ "kills": 333
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 60,
+ "displayed_kills": 10
+ }
+ }
+ },
+ "defender": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 25,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_07.json
===================================================================
--- testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_07.json prev run
+++ testcases/emulator_verified/ahmose_c2_discriminators/sonya_vs_ahmose_07.json this run
@@ -0,0 +1,166 @@
+[
+ {
+ "test_id": "sonya_vs_ahmose_07",
+ "description": "C2 battle report 2734693171178496",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Sonya": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 550,
+ "lancer_t6": 250,
+ "marksman_t6": 100
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 369.5,
+ "defense": 368.8,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 950
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "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-22T13:02:11.000Z",
+ "randomseed": 1790082132,
+ "attacker": 0,
+ "defender": 133,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171178496",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T13-02-39.102Z-807088f1-f3f0-4607-b064-c4d00fe444be.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 900,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 316,
+ "lightly_injured": 584,
+ "kills": 286
+ },
+ "defender": {
+ "troops": 950,
+ "survivors": 133,
+ "losses": 0,
+ "injured": 286,
+ "lightly_injured": 531,
+ "kills": 316
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 550,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 193,
+ "lightly_injured": 357,
+ "kills": 30
+ },
+ "lancer_t6": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 152
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 104
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 950,
+ "survivors": 133,
+ "losses": 0,
+ "injured": 286,
+ "lightly_injured": 531,
+ "kills": 316
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 58,
+ "displayed_kills": 12
+ }
+ }
+ },
+ "defender": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 25,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_gordon_renee/ahmose_bradley_vs_wuming.json
===================================================================
--- testcases/emulator_verified/ahmose_gordon_renee/ahmose_bradley_vs_wuming.json prev run
+++ testcases/emulator_verified/ahmose_gordon_renee/ahmose_bradley_vs_wuming.json this run
@@ -0,0 +1,154 @@
+[
+ {
+ "test_id": "ahmose_bradley_vs_wuming",
+ "description": "C2 battle report 2734693171229499",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Bradley": {
+ "skill_1": 5,
+ "skill_2": 4,
+ "skill_3": 3,
+ "skill_4": 1
+ }
+ },
+ "troops": {
+ "infantry_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 281.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 734.42,
+ "defense": 740.42,
+ "lethality": 375.19,
+ "health": 331.96
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Wu Ming": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 1350
+ },
+ "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-22T19:47:56.000Z",
+ "randomseed": 1790106477,
+ "attacker": 22,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171229499",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T19-48-12.093Z-06182c38-1c44-40a2-a58b-1fe14cded29a.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 22,
+ "losses": 0,
+ "injured": 343,
+ "lightly_injured": 635,
+ "kills": 473
+ },
+ "defender": {
+ "troops": 1350,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 473,
+ "lightly_injured": 877,
+ "kills": 343
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 22,
+ "losses": 0,
+ "injured": 343,
+ "lightly_injured": 635,
+ "kills": 473
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1350,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 473,
+ "lightly_injured": 877,
+ "kills": 343
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 82,
+ "displayed_kills": null
+ }
+ },
+ "Bradley": {
+ "Tactical Assistance": {
+ "triggered": 103,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_gordon_renee/ahmose_gordon_vs_jasser_control.json
===================================================================
--- testcases/emulator_verified/ahmose_gordon_renee/ahmose_gordon_vs_jasser_control.json prev run
+++ testcases/emulator_verified/ahmose_gordon_renee/ahmose_gordon_vs_jasser_control.json this run
@@ -0,0 +1,169 @@
+[
+ {
+ "test_id": "ahmose_gordon_vs_jasser_control",
+ "description": "C2 battle report 2734693171228925",
+ "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": 800,
+ "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": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "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": 297.4,
+ "defense": 286.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-22T19:35:25.000Z",
+ "randomseed": 1790105726,
+ "attacker": 1047,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171228925",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T19-35-41.097Z-cf5e92d1-3386-4d3e-9306-eebfb4dc1b35.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1100,
+ "survivors": 1047,
+ "losses": 0,
+ "injured": 19,
+ "lightly_injured": 34,
+ "kills": 350
+ },
+ "defender": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 19
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 800,
+ "survivors": 747,
+ "losses": 0,
+ "injured": 19,
+ "lightly_injured": 34,
+ "kills": 125
+ },
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 300,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 225
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 19
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 8,
+ "displayed_kills": null
+ }
+ },
+ "Gordon": {
+ "Venom Infusion": {
+ "triggered": 20,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 13,
+ "displayed_kills": null
+ },
+ "Toxic Release": {
+ "triggered": 10,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_gordon_renee/ahmose_gordon_vs_wuming.json
===================================================================
--- testcases/emulator_verified/ahmose_gordon_renee/ahmose_gordon_vs_wuming.json prev run
+++ testcases/emulator_verified/ahmose_gordon_renee/ahmose_gordon_vs_wuming.json this run
@@ -0,0 +1,189 @@
+[
+ {
+ "test_id": "ahmose_gordon_vs_wuming",
+ "description": "C2 battle report 2734693171229289",
+ "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": 300,
+ "lancer_t6": 1200
+ },
+ "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": 900,
+ "lancer_t6": 300,
+ "marksman_t6": 300
+ },
+ "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-22T19:43:36.000Z",
+ "randomseed": 1790106217,
+ "attacker": 140,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171229289",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T19-43-52.098Z-de42fee8-50c1-4f26-a45b-c802bfb3c10e.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1500,
+ "survivors": 140,
+ "losses": 0,
+ "injured": 476,
+ "lightly_injured": 884,
+ "kills": 525
+ },
+ "defender": {
+ "troops": 1500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 525,
+ "lightly_injured": 975,
+ "kills": 476
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 16
+ },
+ "lancer_t6": {
+ "troops": 1200,
+ "survivors": 140,
+ "losses": 0,
+ "injured": 371,
+ "lightly_injured": 689,
+ "kills": 509
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 900,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 315,
+ "lightly_injured": 585,
+ "kills": 87
+ },
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 154
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 235
+ }
+ }
+ },
+ "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_renee/ahmose_gordon_wayne_pure_lancers_vs_jasser.json
===================================================================
--- testcases/emulator_verified/ahmose_gordon_renee/ahmose_gordon_wayne_pure_lancers_vs_jasser.json prev run
+++ testcases/emulator_verified/ahmose_gordon_renee/ahmose_gordon_wayne_pure_lancers_vs_jasser.json this run
@@ -0,0 +1,175 @@
+[
+ {
+ "test_id": "ahmose_gordon_wayne_pure_lancers_vs_jasser",
+ "description": "C2 battle report 2734693171229777",
+ "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
+ },
+ "Wayne": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "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": 421.39,
+ "defense": 427.39,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 900
+ },
+ "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-22T19:56:25.000Z",
+ "randomseed": 1790106986,
+ "attacker": 0,
+ "defender": 21,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171229777",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T19-56-41.089Z-ed4aef91-632f-45a8-878b-192f58ed94ef.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 308
+ },
+ "defender": {
+ "troops": 900,
+ "survivors": 21,
+ "losses": 0,
+ "injured": 308,
+ "lightly_injured": 571,
+ "kills": 105
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 308
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 900,
+ "survivors": 21,
+ "losses": 0,
+ "injured": 308,
+ "lightly_injured": 571,
+ "kills": 105
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Wayne": {
+ "Roundabout Hit": {
+ "triggered": null,
+ "displayed_kills": null
+ },
+ "Thunder Strike": {
+ "triggered": 43,
+ "displayed_kills": 22
+ }
+ },
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Gordon": {
+ "Venom Infusion": {
+ "triggered": 86,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 57,
+ "displayed_kills": null
+ },
+ "Toxic Release": {
+ "triggered": 43,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_gordon_renee/ahmose_gordon_wayne_vs_jasser.json
===================================================================
--- testcases/emulator_verified/ahmose_gordon_renee/ahmose_gordon_wayne_vs_jasser.json prev run
+++ testcases/emulator_verified/ahmose_gordon_renee/ahmose_gordon_wayne_vs_jasser.json this run
@@ -0,0 +1,193 @@
+[
+ {
+ "test_id": "ahmose_gordon_wayne_vs_jasser",
+ "description": "C2 battle report 2734693171229258",
+ "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
+ },
+ "Wayne": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 300,
+ "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": 421.39,
+ "defense": 427.39,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "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": 297.4,
+ "defense": 286.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-22T19:42:26.000Z",
+ "randomseed": 1790106147,
+ "attacker": 0,
+ "defender": 327,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171229258",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T19-42-42.089Z-be88920c-6660-44b1-b166-e5d6dfa22f1b.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 411
+ },
+ "defender": {
+ "troops": 1500,
+ "survivors": 327,
+ "losses": 0,
+ "injured": 411,
+ "lightly_injured": 762,
+ "kills": 210
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 55
+ },
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 356
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 24
+ },
+ "marksman_t6": {
+ "troops": 500,
+ "survivors": 327,
+ "losses": 0,
+ "injured": 61,
+ "lightly_injured": 112,
+ "kills": 186
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Wayne": {
+ "Roundabout Hit": {
+ "triggered": null,
+ "displayed_kills": null
+ },
+ "Thunder Strike": {
+ "triggered": 30,
+ "displayed_kills": 31
+ }
+ },
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 10,
+ "displayed_kills": null
+ }
+ },
+ "Gordon": {
+ "Venom Infusion": {
+ "triggered": 36,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 24,
+ "displayed_kills": null
+ },
+ "Toxic Release": {
+ "triggered": 18,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_gordon_renee/ahmose_renee_vs_jasser_addition.json
===================================================================
--- testcases/emulator_verified/ahmose_gordon_renee/ahmose_renee_vs_jasser_addition.json prev run
+++ testcases/emulator_verified/ahmose_gordon_renee/ahmose_renee_vs_jasser_addition.json this run
@@ -0,0 +1,170 @@
+[
+ {
+ "test_id": "ahmose_renee_vs_jasser_addition",
+ "description": "C2 battle report 2734693171229602",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 200,
+ "lancer_t6": 150
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "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": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_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": 297.4,
+ "defense": 286.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-22T19:51:15.000Z",
+ "randomseed": 1790106676,
+ "attacker": 0,
+ "defender": 320,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171229602",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T19-51-31.092Z-606ba725-4bd3-4869-bb85-4dcc3998391d.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 350,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 123,
+ "lightly_injured": 227,
+ "kills": 343
+ },
+ "defender": {
+ "troops": 1300,
+ "survivors": 320,
+ "losses": 0,
+ "injured": 343,
+ "lightly_injured": 637,
+ "kills": 123
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 43
+ },
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 300
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 20,
+ "losses": 0,
+ "injured": 343,
+ "lightly_injured": 637,
+ "kills": 22
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 300,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 101
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 9,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 34,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_gordon_renee/ahmose_renee_vs_jasser_control.json
===================================================================
--- testcases/emulator_verified/ahmose_gordon_renee/ahmose_renee_vs_jasser_control.json prev run
+++ testcases/emulator_verified/ahmose_gordon_renee/ahmose_renee_vs_jasser_control.json this run
@@ -0,0 +1,161 @@
+[
+ {
+ "test_id": "ahmose_renee_vs_jasser_control",
+ "description": "C2 battle report 2734693171229020",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 800,
+ "lancer_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "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": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "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": 297.4,
+ "defense": 286.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-22T19:37:09.000Z",
+ "randomseed": 1790105830,
+ "attacker": 1067,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171229020",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T19-37-25.104Z-b438d950-d174-47a7-9e11-1fd4fdd6e7aa.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1100,
+ "survivors": 1067,
+ "losses": 0,
+ "injured": 12,
+ "lightly_injured": 21,
+ "kills": 350
+ },
+ "defender": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 12
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 800,
+ "survivors": 767,
+ "losses": 0,
+ "injured": 12,
+ "lightly_injured": 21,
+ "kills": 100
+ },
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 300,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 250
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 12
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 4,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 11,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_gordon_renee/ahmose_renee_vs_jasser_mark_kind.json
===================================================================
--- testcases/emulator_verified/ahmose_gordon_renee/ahmose_renee_vs_jasser_mark_kind.json prev run
+++ testcases/emulator_verified/ahmose_gordon_renee/ahmose_renee_vs_jasser_mark_kind.json this run
@@ -0,0 +1,170 @@
+[
+ {
+ "test_id": "ahmose_renee_vs_jasser_mark_kind",
+ "description": "C2 battle report 2734693171229547",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 1000,
+ "lancer_t6": 150
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "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": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_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": 297.4,
+ "defense": 286.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-22T19:49:24.000Z",
+ "randomseed": 1790106565,
+ "attacker": 937,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171229547",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T19-49-40.089Z-3411a2d5-f80c-458b-b110-c099b8af3982.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1150,
+ "survivors": 937,
+ "losses": 0,
+ "injured": 75,
+ "lightly_injured": 138,
+ "kills": 455
+ },
+ "defender": {
+ "troops": 1300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 455,
+ "lightly_injured": 845,
+ "kills": 75
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 787,
+ "losses": 0,
+ "injured": 75,
+ "lightly_injured": 138,
+ "kills": 169
+ },
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 150,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 286
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 17
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 58
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 6,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 14,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_gordon_renee/ahmose_wayne_vs_jasser.json
===================================================================
--- testcases/emulator_verified/ahmose_gordon_renee/ahmose_wayne_vs_jasser.json prev run
+++ testcases/emulator_verified/ahmose_gordon_renee/ahmose_wayne_vs_jasser.json this run
@@ -0,0 +1,155 @@
+[
+ {
+ "test_id": "ahmose_wayne_vs_jasser",
+ "description": "C2 battle report 2734693171229364",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Wayne": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 550
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 281.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 421.39,
+ "defense": 427.39,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "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": 297.4,
+ "defense": 286.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-22T19:45:01.000Z",
+ "randomseed": 1790106302,
+ "attacker": 0,
+ "defender": 141,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171229364",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T19-45-17.099Z-390f5034-d940-451a-992f-9b3df252c21c.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 550,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 193,
+ "lightly_injured": 357,
+ "kills": 476
+ },
+ "defender": {
+ "troops": 1500,
+ "survivors": 141,
+ "losses": 0,
+ "injured": 476,
+ "lightly_injured": 883,
+ "kills": 193
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 550,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 193,
+ "lightly_injured": 357,
+ "kills": 476
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1500,
+ "survivors": 141,
+ "losses": 0,
+ "injured": 476,
+ "lightly_injured": 883,
+ "kills": 193
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Wayne": {
+ "Roundabout Hit": {
+ "triggered": null,
+ "displayed_kills": null
+ },
+ "Thunder Strike": {
+ "triggered": 115,
+ "displayed_kills": 40
+ }
+ },
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 92,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_gordon_renee/ahmose_wayne_vs_wuming.json
===================================================================
--- testcases/emulator_verified/ahmose_gordon_renee/ahmose_wayne_vs_wuming.json prev run
+++ testcases/emulator_verified/ahmose_gordon_renee/ahmose_wayne_vs_wuming.json this run
@@ -0,0 +1,157 @@
+[
+ {
+ "test_id": "ahmose_wayne_vs_wuming",
+ "description": "C2 battle report 2734693171229424",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Wayne": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 281.92,
+ "defense": 286.17,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 421.39,
+ "defense": 427.39,
+ "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": 1050
+ },
+ "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-22T19:46:29.000Z",
+ "randomseed": 1790106390,
+ "attacker": 0,
+ "defender": 50,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171229424",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T19-46-45.093Z-c16372d3-8764-4387-945b-7b3024675da9.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 350
+ },
+ "defender": {
+ "troops": 1050,
+ "survivors": 50,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 350
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1050,
+ "survivors": 50,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Wayne": {
+ "Roundabout Hit": {
+ "triggered": null,
+ "displayed_kills": null
+ },
+ "Thunder Strike": {
+ "triggered": 111,
+ "displayed_kills": 26
+ }
+ },
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 89,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_gordon_renee/wuming_renee_vs_jasser.json
===================================================================
--- testcases/emulator_verified/ahmose_gordon_renee/wuming_renee_vs_jasser.json prev run
+++ testcases/emulator_verified/ahmose_gordon_renee/wuming_renee_vs_jasser.json this run
@@ -0,0 +1,164 @@
+[
+ {
+ "test_id": "wuming_renee_vs_jasser",
+ "description": "C2 battle report 2734693171229643",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Wu Ming": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 2,
+ "skill_4": 0
+ },
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 200,
+ "lancer_t6": 150
+ },
+ "stats": {
+ "infantry": {
+ "attack": 508.98,
+ "defense": 501.98,
+ "lethality": 250.71,
+ "health": 274.79
+ },
+ "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": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_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": 297.4,
+ "defense": 286.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-22T19:52:37.000Z",
+ "randomseed": 1790106758,
+ "attacker": 176,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171229643",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T19-52-53.093Z-a798fb63-9966-43c9-a6b3-2faea7d87a9c.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 350,
+ "survivors": 176,
+ "losses": 0,
+ "injured": 61,
+ "lightly_injured": 113,
+ "kills": 455
+ },
+ "defender": {
+ "troops": 1300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 455,
+ "lightly_injured": 845,
+ "kills": 61
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 26,
+ "losses": 0,
+ "injured": 61,
+ "lightly_injured": 113,
+ "kills": 64
+ },
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 150,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 391
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 13
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 48
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 32,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_isolation_20260925/ahmose_1000i_vs_wuming_1000i.json
===================================================================
--- testcases/emulator_verified/ahmose_isolation_20260925/ahmose_1000i_vs_wuming_1000i.json prev run
+++ testcases/emulator_verified/ahmose_isolation_20260925/ahmose_1000i_vs_wuming_1000i.json this run
@@ -0,0 +1,142 @@
+[
+ {
+ "test_id": "ahmose_1000i_vs_wuming_1000i",
+ "description": "C2 battle report 2734693171342058",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 422.61,
+ "defense": 405.61,
+ "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": {
+ "Wu Ming": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 1000
+ },
+ "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-25T05:36:02.000Z",
+ "randomseed": 1790314563,
+ "attacker": 0,
+ "defender": 82,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171342058",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T05-36-18.192Z-96efaf92-abb7-4e28-b03f-e99c019d96b2.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 322
+ },
+ "defender": {
+ "troops": 1000,
+ "survivors": 82,
+ "losses": 0,
+ "injured": 322,
+ "lightly_injured": 596,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 322
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 82,
+ "losses": 0,
+ "injured": 322,
+ "lightly_injured": 596,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 86,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/ahmose_isolation_20260925/ahmose_bradley_1000i_vs_3700i.json
===================================================================
--- testcases/emulator_verified/ahmose_isolation_20260925/ahmose_bradley_1000i_vs_3700i.json prev run
+++ testcases/emulator_verified/ahmose_isolation_20260925/ahmose_bradley_1000i_vs_3700i.json this run
@@ -0,0 +1,147 @@
+[
+ {
+ "test_id": "ahmose_bradley_1000i_vs_3700i",
+ "description": "C2 battle report 2734693171341989",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Bradley": {
+ "skill_1": 5,
+ "skill_2": 4,
+ "skill_3": 3,
+ "skill_4": 1
+ }
+ },
+ "troops": {
+ "infantry_t6": 1000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 422.61,
+ "defense": 405.61,
+ "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": 3700
+ },
+ "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-25T05:34:09.000Z",
+ "randomseed": 1790314450,
+ "attacker": 0,
+ "defender": 77,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171341989",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T05-34-25.223Z-c513bff5-30c5-4e1a-87ec-4e00ce0d0b8e.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 1269
+ },
+ "defender": {
+ "troops": 3700,
+ "survivors": 77,
+ "losses": 0,
+ "injured": 1269,
+ "lightly_injured": 2354,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 1269
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 3700,
+ "survivors": 77,
+ "losses": 0,
+ "injured": 1269,
+ "lightly_injured": 2354,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 97,
+ "displayed_kills": null
+ }
+ },
+ "Bradley": {
+ "Tactical Assistance": {
+ "triggered": 121,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gatot_sonya_hendrik_precise.json
===================================================================
--- testcases/emulator_verified/gatot_sonya_hendrik_precise.json prev run
+++ testcases/emulator_verified/gatot_sonya_hendrik_precise.json this run
@@ -0,0 +1,239 @@
+[
+ {
+ "test_id": "c2_2734693170978104",
+ "description": "C2 battle report 2734693170978104",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gatot": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Sonya": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ },
+ "Hendrik": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 300,
+ "lancer_t6": 300,
+ "marksman_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 623.17,
+ "defense": 619.79,
+ "lethality": 228.98,
+ "health": 279.34
+ },
+ "lancer": {
+ "attack": 369.36,
+ "defense": 368.73,
+ "lethality": 186.82,
+ "health": 181.88
+ },
+ "marksman": {
+ "attack": 478.86,
+ "defense": 467.73,
+ "lethality": 244.02,
+ "health": 220.98
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gatot": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Sonya": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ },
+ "Hendrik": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 300,
+ "lancer_t6": 300,
+ "marksman_t6": 300
+ },
+ "stats": {
+ "infantry": {
+ "attack": 665.38,
+ "defense": 662.63,
+ "lethality": 316.11,
+ "health": 338.06
+ },
+ "lancer": {
+ "attack": 529.96,
+ "defense": 533.71,
+ "lethality": 323.16,
+ "health": 285.53
+ },
+ "marksman": {
+ "attack": 439.96,
+ "defense": 445.46,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-18T05:10:41.000Z",
+ "randomseed": 1789708242,
+ "attacker": 0,
+ "defender": 367,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693170978104",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../c2/data/runs/2026-09-18T05-09-57.549Z.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 900,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 315,
+ "lightly_injured": 585,
+ "kills": 188
+ },
+ "defender": {
+ "troops": 900,
+ "survivors": 367,
+ "losses": 0,
+ "injured": 188,
+ "lightly_injured": 345,
+ "kills": 315
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 1
+ },
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 21
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 166
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 300,
+ "survivors": 5,
+ "losses": 0,
+ "injured": 104,
+ "lightly_injured": 191,
+ "kills": 18
+ },
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 215,
+ "losses": 0,
+ "injured": 30,
+ "lightly_injured": 55,
+ "kills": 176
+ },
+ "marksman_t6": {
+ "troops": 300,
+ "survivors": 147,
+ "losses": 0,
+ "injured": 54,
+ "lightly_injured": 99,
+ "kills": 121
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 25,
+ "displayed_kills": 2
+ }
+ },
+ "Hendrik": {
+ "Dagon's Heir": {
+ "triggered": 19,
+ "displayed_kills": 89
+ },
+ "Armor of Barnacles": {
+ "triggered": 14,
+ "displayed_kills": null
+ }
+ },
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": 37,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 29,
+ "displayed_kills": 11
+ },
+ "Torrential Impact": {
+ "triggered": 11,
+ "displayed_kills": 14
+ }
+ },
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": 58,
+ "displayed_kills": null
+ }
+ },
+ "Hendrik": {
+ "Armor of Barnacles": {
+ "triggered": 14,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gordon_interactions_20260925/gordon_wayne_1200l_vs_4800l.json
===================================================================
--- testcases/emulator_verified/gordon_interactions_20260925/gordon_wayne_1200l_vs_4800l.json prev run
+++ testcases/emulator_verified/gordon_interactions_20260925/gordon_wayne_1200l_vs_4800l.json this run
@@ -0,0 +1,158 @@
+[
+ {
+ "test_id": "gordon_wayne_1200l_vs_4800l",
+ "description": "C2 battle report 2734693171342597",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ },
+ "Wayne": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 1200
+ },
+ "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": 426.39,
+ "defense": 422.39,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 4800
+ },
+ "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-25T06:14:41.000Z",
+ "randomseed": 1790316882,
+ "attacker": 0,
+ "defender": 313,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171342597",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T06-14-57.221Z-3bf37809-1abc-47ba-b785-c77cc590787c.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 420,
+ "lightly_injured": 780,
+ "kills": 1571
+ },
+ "defender": {
+ "troops": 4800,
+ "survivors": 313,
+ "losses": 0,
+ "injured": 1571,
+ "lightly_injured": 2916,
+ "kills": 420
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 420,
+ "lightly_injured": 780,
+ "kills": 1571
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 4800,
+ "survivors": 313,
+ "losses": 0,
+ "injured": 1571,
+ "lightly_injured": 2916,
+ "kills": 420
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Wayne": {
+ "Roundabout Hit": {
+ "triggered": null,
+ "displayed_kills": null
+ },
+ "Thunder Strike": {
+ "triggered": 16,
+ "displayed_kills": 115
+ }
+ },
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 16,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 32,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 21,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gordon_interactions_20260925/gordon_wayne_800l_vs_3200l.json
===================================================================
--- testcases/emulator_verified/gordon_interactions_20260925/gordon_wayne_800l_vs_3200l.json prev run
+++ testcases/emulator_verified/gordon_interactions_20260925/gordon_wayne_800l_vs_3200l.json this run
@@ -0,0 +1,158 @@
+[
+ {
+ "test_id": "gordon_wayne_800l_vs_3200l",
+ "description": "C2 battle report 2734693171342685",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ },
+ "Wayne": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 800
+ },
+ "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": 426.39,
+ "defense": 422.39,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 3200
+ },
+ "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-25T06:21:27.000Z",
+ "randomseed": 1790317288,
+ "attacker": 0,
+ "defender": 207,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171342685",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T06-21-43.168Z-a2ec92c0-70f6-46df-87e5-9726a2d79078.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 800,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 280,
+ "lightly_injured": 520,
+ "kills": 1048
+ },
+ "defender": {
+ "troops": 3200,
+ "survivors": 207,
+ "losses": 0,
+ "injured": 1048,
+ "lightly_injured": 1945,
+ "kills": 280
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 800,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 280,
+ "lightly_injured": 520,
+ "kills": 1048
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 3200,
+ "survivors": 207,
+ "losses": 0,
+ "injured": 1048,
+ "lightly_injured": 1945,
+ "kills": 280
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Wayne": {
+ "Roundabout Hit": {
+ "triggered": null,
+ "displayed_kills": null
+ },
+ "Thunder Strike": {
+ "triggered": 16,
+ "displayed_kills": 77
+ }
+ },
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 16,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 32,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 21,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gordon_interactions_20260925/wayne_control_1000l_vs_1700l.json
===================================================================
--- testcases/emulator_verified/gordon_interactions_20260925/wayne_control_1000l_vs_1700l.json prev run
+++ testcases/emulator_verified/gordon_interactions_20260925/wayne_control_1000l_vs_1700l.json this run
@@ -0,0 +1,138 @@
+[
+ {
+ "test_id": "wayne_control_1000l_vs_1700l",
+ "description": "C2 battle report 2734693171342643",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Wayne": {
+ "skill_1": 2,
+ "skill_2": 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": 426.39,
+ "defense": 422.39,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "lancer_t6": 1700
+ },
+ "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-25T06:16:54.000Z",
+ "randomseed": 1790317015,
+ "attacker": 0,
+ "defender": 21,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171342643",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T06-17-10.150Z-1d7cb8fe-496f-4f61-86aa-a9b8c7c42f6d.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 588
+ },
+ "defender": {
+ "troops": 1700,
+ "survivors": 21,
+ "losses": 0,
+ "injured": 588,
+ "lightly_injured": 1091,
+ "kills": 350
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 350,
+ "lightly_injured": 650,
+ "kills": 588
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 1700,
+ "survivors": 21,
+ "losses": 0,
+ "injured": 588,
+ "lightly_injured": 1091,
+ "kills": 350
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Wayne": {
+ "Roundabout Hit": {
+ "triggered": null,
+ "displayed_kills": null
+ },
+ "Thunder Strike": {
+ "triggered": 16,
+ "displayed_kills": 51
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gordon_isolation_20260925/gordon_solo_1329l_vs_5000l.json
===================================================================
--- testcases/emulator_verified/gordon_isolation_20260925/gordon_solo_1329l_vs_5000l.json prev run
+++ testcases/emulator_verified/gordon_isolation_20260925/gordon_solo_1329l_vs_5000l.json this run
@@ -0,0 +1,143 @@
+[
+ {
+ "test_id": "gordon_solo_1329l_vs_5000l",
+ "description": "C2 battle report 2734693171341664",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 1329
+ },
+ "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_t6": 5000
+ },
+ "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-25T05:14:22.000Z",
+ "randomseed": 1790313263,
+ "attacker": 0,
+ "defender": 48,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171341664",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T05-14-38.197Z-8d1d14e8-8e60-4737-ac95-88710595908c.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1329,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 466,
+ "lightly_injured": 863,
+ "kills": 1734
+ },
+ "defender": {
+ "troops": 5000,
+ "survivors": 48,
+ "losses": 0,
+ "injured": 1734,
+ "lightly_injured": 3218,
+ "kills": 466
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1329,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 466,
+ "lightly_injured": 863,
+ "kills": 1734
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 5000,
+ "survivors": 48,
+ "losses": 0,
+ "injured": 1734,
+ "lightly_injured": 3218,
+ "kills": 466
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 18,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 37,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 24,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gordon_isolation_20260925/gordon_solo_180l_vs_700l.json
===================================================================
--- testcases/emulator_verified/gordon_isolation_20260925/gordon_solo_180l_vs_700l.json prev run
+++ testcases/emulator_verified/gordon_isolation_20260925/gordon_solo_180l_vs_700l.json this run
@@ -0,0 +1,143 @@
+[
+ {
+ "test_id": "gordon_solo_180l_vs_700l",
+ "description": "C2 battle report 2734693171341527",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 180
+ },
+ "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_t6": 700
+ },
+ "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-25T05:08:41.000Z",
+ "randomseed": 1790312922,
+ "attacker": 0,
+ "defender": 87,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171341527",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T05-08-57.209Z-36d9e066-50bd-4705-a318-219ee52b9cde.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 180,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 63,
+ "lightly_injured": 117,
+ "kills": 215
+ },
+ "defender": {
+ "troops": 700,
+ "survivors": 87,
+ "losses": 0,
+ "injured": 215,
+ "lightly_injured": 398,
+ "kills": 63
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 180,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 63,
+ "lightly_injured": 117,
+ "kills": 215
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 700,
+ "survivors": 87,
+ "losses": 0,
+ "injured": 215,
+ "lightly_injured": 398,
+ "kills": 63
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 15,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 30,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 20,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gordon_isolation_20260925/gordon_solo_1860l_vs_7000l.json
===================================================================
--- testcases/emulator_verified/gordon_isolation_20260925/gordon_solo_1860l_vs_7000l.json prev run
+++ testcases/emulator_verified/gordon_isolation_20260925/gordon_solo_1860l_vs_7000l.json this run
@@ -0,0 +1,143 @@
+[
+ {
+ "test_id": "gordon_solo_1860l_vs_7000l",
+ "description": "C2 battle report 2734693171341599",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gordon": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 2,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 1860
+ },
+ "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_t6": 7000
+ },
+ "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-25T05:11:58.000Z",
+ "randomseed": 1790313119,
+ "attacker": 0,
+ "defender": 87,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171341599",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T05-12-14.175Z-267a9f20-5b01-4fc0-bd80-6897148d3bc3.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1860,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 651,
+ "lightly_injured": 1209,
+ "kills": 2420
+ },
+ "defender": {
+ "troops": 7000,
+ "survivors": 87,
+ "losses": 0,
+ "injured": 2420,
+ "lightly_injured": 4493,
+ "kills": 651
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1860,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 651,
+ "lightly_injured": 1209,
+ "kills": 2420
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 7000,
+ "survivors": 87,
+ "losses": 0,
+ "injured": 2420,
+ "lightly_injured": 4493,
+ "kills": 651
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 18,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 36,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 24,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gordon_isolation_20260925/nohero_300l_vs_600l.json
===================================================================
--- testcases/emulator_verified/gordon_isolation_20260925/nohero_300l_vs_600l.json prev run
+++ testcases/emulator_verified/gordon_isolation_20260925/nohero_300l_vs_600l.json this run
@@ -0,0 +1,121 @@
+[
+ {
+ "test_id": "nohero_300l_vs_600l",
+ "description": "C2 battle report 2734693171341609",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "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": 288.42,
+ "defense": 284.42,
+ "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-25T05:13:14.000Z",
+ "randomseed": 1790313195,
+ "attacker": 0,
+ "defender": 255,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171341609",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T05-13-30.230Z-95c8ccc6-056c-4a9b-be3e-bf585dba13a8.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 121
+ },
+ "defender": {
+ "troops": 600,
+ "survivors": 255,
+ "losses": 0,
+ "injured": 121,
+ "lightly_injured": 224,
+ "kills": 105
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 121
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 600,
+ "survivors": 255,
+ "losses": 0,
+ "injured": 121,
+ "lightly_injured": 224,
+ "kills": 105
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_blastmaster_replay_550m_vs_810i_45l_45m.json
===================================================================
--- testcases/emulator_verified/gwen_blastmaster_replay_550m_vs_810i_45l_45m.json prev run
+++ testcases/emulator_verified/gwen_blastmaster_replay_550m_vs_810i_45l_45m.json this run
@@ -0,0 +1,98 @@
+[
+ {
+ "test_id": "gwen_blastmaster_replay_550m_vs_810i_45l_45m",
+ "description": "Fresh full-kit Gwen 3/1/1 replay investigating the deterministic Blastmaster cadence discrepancy; both participants and current report stats verified during capture.",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1
+ }
+ },
+ "troops": {
+ "marksman_t6": 550
+ },
+ "stats": {
+ "inf": {
+ "attack": 305.0,
+ "defense": 288.0,
+ "lethality": 254.0,
+ "health": 238.4
+ },
+ "lanc": {
+ "attack": 286.9,
+ "defense": 281.2,
+ "lethality": 234.3,
+ "health": 230.0
+ },
+ "mark": {
+ "attack": 439.8,
+ "defense": 435.8,
+ "lethality": 245.8,
+ "health": 230.3
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 810,
+ "lancer_t6": 45,
+ "marksman_t6": 45
+ },
+ "stats": {
+ "inf": {
+ "attack": 257.1,
+ "defense": 249.7,
+ "lethality": 196.5,
+ "health": 192.0
+ },
+ "lanc": {
+ "attack": 208.7,
+ "defense": 208.0,
+ "lethality": 197.0,
+ "health": 186.1
+ },
+ "mark": {
+ "attack": 231.0,
+ "defense": 219.8,
+ "lethality": 191.5,
+ "health": 177.2
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "game_report_result": [
+ {
+ "attacker": 0,
+ "defender": 172,
+ "metadata": {
+ "source": "emulator",
+ "report_timestamp_displayed": "2026-09-21 10:36:45",
+ "world_coord": { "x": 791, "y": 577 },
+ "winner": "defender",
+ "stat_units": "percentage_points",
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Eagle Vision": { "triggered": 1, "displayed_kills": null },
+ "Air Dominance": { "triggered": 10, "displayed_kills": 7 },
+ "Blastmaster": { "triggered": 13, "displayed_kills": 32 }
+ }
+ }
+ },
+ "evidence": {
+ "overview": "../../docs/mechanics-audit/reviews/attack-target-2026-09-21/gwen-overview.png",
+ "stats": "../../docs/mechanics-audit/reviews/attack-target-2026-09-21/gwen-stats.png",
+ "skill_counters": "../../docs/mechanics-audit/reviews/attack-target-2026-09-21/gwen-skill-counters.png",
+ "parsed_report": "../../docs/mechanics-audit/reviews/attack-target-2026-09-21/gwen-report.json"
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_c2_twenty/ahmose_gordon_gwen_vs_gatot_patrick_hendrik.json
===================================================================
--- testcases/emulator_verified/gwen_c2_twenty/ahmose_gordon_gwen_vs_gatot_patrick_hendrik.json prev run
+++ testcases/emulator_verified/gwen_c2_twenty/ahmose_gordon_gwen_vs_gatot_patrick_hendrik.json this run
@@ -0,0 +1,225 @@
+[
+ {
+ "test_id": "ahmose_gordon_gwen_vs_gatot_patrick_hendrik",
+ "description": "C2 battle report 2734693171109692",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ },
+ "Gordon": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 302,
+ "lancer_t6": 340,
+ "marksman_t6": 392
+ },
+ "stats": {
+ "infantry": {
+ "attack": 345.57,
+ "defense": 338.12,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 364.09,
+ "defense": 363.39,
+ "lethality": 191.83,
+ "health": 190.89
+ },
+ "marksman": {
+ "attack": 344.4,
+ "defense": 333.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gatot": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Patrick": {
+ "skill_1": 3,
+ "skill_2": 2
+ },
+ "Hendrik": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 257,
+ "lancer_t6": 337,
+ "marksman_t6": 230
+ },
+ "stats": {
+ "infantry": {
+ "attack": 670.13,
+ "defense": 663.13,
+ "lethality": 316.11,
+ "health": 338.06
+ },
+ "lancer": {
+ "attack": 391.69,
+ "defense": 395.94,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 444.21,
+ "defense": 450.21,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-21T22:32:54.000Z",
+ "randomseed": 1790029975,
+ "attacker": 0,
+ "defender": 764,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171109692",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-21T22-33-22.264Z-9fe5f826-92fd-4ad5-b6d2-13a330adb2ee.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1034,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 363,
+ "lightly_injured": 671,
+ "kills": 21
+ },
+ "defender": {
+ "troops": 824,
+ "survivors": 764,
+ "losses": 0,
+ "injured": 21,
+ "lightly_injured": 39,
+ "kills": 363
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 302,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 106,
+ "lightly_injured": 196,
+ "kills": 0
+ },
+ "lancer_t6": {
+ "troops": 340,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 119,
+ "lightly_injured": 221,
+ "kills": 3
+ },
+ "marksman_t6": {
+ "troops": 392,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 138,
+ "lightly_injured": 254,
+ "kills": 18
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 257,
+ "survivors": 197,
+ "losses": 0,
+ "injured": 21,
+ "lightly_injured": 39,
+ "kills": 75
+ },
+ "lancer_t6": {
+ "troops": 337,
+ "survivors": 337,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 133
+ },
+ "marksman_t6": {
+ "troops": 230,
+ "survivors": 230,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 155
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 9,
+ "displayed_kills": 1
+ }
+ },
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 3,
+ "displayed_kills": null
+ }
+ },
+ "Gordon": {
+ "Venom Infusion": {
+ "triggered": 10,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 8,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": 26,
+ "displayed_kills": null
+ }
+ },
+ "Hendrik": {
+ "Armor of Barnacles": {
+ "triggered": 6,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_c2_twenty/ahmose_renee_gwen_vs_sergey_patrick_hendrik.json
===================================================================
--- testcases/emulator_verified/gwen_c2_twenty/ahmose_renee_gwen_vs_sergey_patrick_hendrik.json prev run
+++ testcases/emulator_verified/gwen_c2_twenty/ahmose_renee_gwen_vs_sergey_patrick_hendrik.json this run
@@ -0,0 +1,225 @@
+[
+ {
+ "test_id": "ahmose_renee_gwen_vs_sergey_patrick_hendrik",
+ "description": "C2 battle report 2734693171109795",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "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": 221,
+ "lancer_t6": 259,
+ "marksman_t6": 311
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "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": {
+ "Sergey": {
+ "skill_1": 2,
+ "skill_2": 2
+ },
+ "Patrick": {
+ "skill_1": 4,
+ "skill_2": 4
+ },
+ "Hendrik": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 377,
+ "lancer_t6": 256,
+ "marksman_t6": 350
+ },
+ "stats": {
+ "infantry": {
+ "attack": 346.14,
+ "defense": 338.69,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 293.2,
+ "defense": 292.5,
+ "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-21T22:46:34.000Z",
+ "randomseed": 1790030795,
+ "attacker": 0,
+ "defender": 252,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171109795",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-21T22-46-51.115Z-443535a5-97f8-4b8d-8b23-1cdba2bfbb5c.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 791,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 278,
+ "lightly_injured": 513,
+ "kills": 257
+ },
+ "defender": {
+ "troops": 983,
+ "survivors": 252,
+ "losses": 0,
+ "injured": 257,
+ "lightly_injured": 474,
+ "kills": 278
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 221,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 78,
+ "lightly_injured": 143,
+ "kills": 8
+ },
+ "lancer_t6": {
+ "troops": 259,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 91,
+ "lightly_injured": 168,
+ "kills": 99
+ },
+ "marksman_t6": {
+ "troops": 311,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 109,
+ "lightly_injured": 202,
+ "kills": 150
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 377,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 132,
+ "lightly_injured": 245,
+ "kills": 7
+ },
+ "lancer_t6": {
+ "troops": 256,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 90,
+ "lightly_injured": 166,
+ "kills": 33
+ },
+ "marksman_t6": {
+ "troops": 350,
+ "survivors": 252,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 63,
+ "kills": 238
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 8,
+ "displayed_kills": 5
+ },
+ "Blastmaster": {
+ "triggered": 5,
+ "displayed_kills": 5
+ }
+ },
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 2,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 13,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Hendrik": {
+ "Dagon's Heir": {
+ "triggered": 9,
+ "displayed_kills": 46
+ },
+ "Armor of Barnacles": {
+ "triggered": 6,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_c2_twenty/edith_patrick_gwen_vs_ahmose_renee_bradley.json
===================================================================
--- testcases/emulator_verified/gwen_c2_twenty/edith_patrick_gwen_vs_ahmose_renee_bradley.json prev run
+++ testcases/emulator_verified/gwen_c2_twenty/edith_patrick_gwen_vs_ahmose_renee_bradley.json this run
@@ -0,0 +1,222 @@
+[
+ {
+ "test_id": "edith_patrick_gwen_vs_ahmose_renee_bradley",
+ "description": "C2 battle report 2734693171109940",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Edith": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Patrick": {
+ "skill_1": 3,
+ "skill_2": 2
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 362,
+ "lancer_t6": 400,
+ "marksman_t6": 251
+ },
+ "stats": {
+ "infantry": {
+ "attack": 521.6,
+ "defense": 514.6,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 391.69,
+ "defense": 395.94,
+ "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": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ },
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Bradley": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 1
+ }
+ },
+ "troops": {
+ "infantry_t6": 317,
+ "lancer_t6": 397,
+ "marksman_t6": 290
+ },
+ "stats": {
+ "infantry": {
+ "attack": 345.57,
+ "defense": 338.12,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 482.4,
+ "defense": 481.7,
+ "lethality": 252.8,
+ "health": 239.41
+ },
+ "marksman": {
+ "attack": 681.96,
+ "defense": 670.76,
+ "lethality": 322.27,
+ "health": 273.25
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-21T22:58:10.000Z",
+ "randomseed": 1790031491,
+ "attacker": 0,
+ "defender": 461,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171109940",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-21T22-58-27.117Z-51ecb336-999c-4119-b6ea-2ac7e5b0babc.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1013,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 355,
+ "lightly_injured": 658,
+ "kills": 191
+ },
+ "defender": {
+ "troops": 1004,
+ "survivors": 461,
+ "losses": 0,
+ "injured": 191,
+ "lightly_injured": 352,
+ "kills": 355
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 362,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 127,
+ "lightly_injured": 235,
+ "kills": 11
+ },
+ "lancer_t6": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 69
+ },
+ "marksman_t6": {
+ "troops": 251,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 163,
+ "kills": 111
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 317,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 111,
+ "lightly_injured": 206,
+ "kills": 5
+ },
+ "lancer_t6": {
+ "troops": 397,
+ "survivors": 176,
+ "losses": 0,
+ "injured": 78,
+ "lightly_injured": 143,
+ "kills": 135
+ },
+ "marksman_t6": {
+ "troops": 290,
+ "survivors": 285,
+ "losses": 0,
+ "injured": 2,
+ "lightly_injured": 3,
+ "kills": 215
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 7,
+ "displayed_kills": 4
+ },
+ "Blastmaster": {
+ "triggered": 3,
+ "displayed_kills": 4
+ }
+ }
+ },
+ "defender": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 2,
+ "displayed_kills": null
+ }
+ },
+ "Bradley": {
+ "Tactical Assistance": {
+ "triggered": 4,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 9,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_c2_twenty/flint_patrick_gwen_vs_ahmose_renee_jasser.json
===================================================================
--- testcases/emulator_verified/gwen_c2_twenty/flint_patrick_gwen_vs_ahmose_renee_jasser.json prev run
+++ testcases/emulator_verified/gwen_c2_twenty/flint_patrick_gwen_vs_ahmose_renee_jasser.json this run
@@ -0,0 +1,209 @@
+[
+ {
+ "test_id": "flint_patrick_gwen_vs_ahmose_renee_jasser",
+ "description": "C2 battle report 2734693171109744",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Flint": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ },
+ "Patrick": {
+ "skill_1": 4,
+ "skill_2": 4
+ },
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 242,
+ "lancer_t6": 280,
+ "marksman_t6": 332
+ },
+ "stats": {
+ "infantry": {
+ "attack": 326.34,
+ "defense": 318.89,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 293.2,
+ "defense": 292.5,
+ "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": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Jasser": {
+ "skill_1": 4,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 398,
+ "lancer_t6": 277,
+ "marksman_t6": 371
+ },
+ "stats": {
+ "infantry": {
+ "attack": 417.61,
+ "defense": 410.61,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 607.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 354.37,
+ "defense": 360.37,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-21T22:37:44.000Z",
+ "randomseed": 1790030265,
+ "attacker": 0,
+ "defender": 917,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171109744",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-21T22-38-12.117Z-1c4565ff-8a6b-4e3e-a6e4-9865a6a47c42.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 854,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 300,
+ "lightly_injured": 554,
+ "kills": 46
+ },
+ "defender": {
+ "troops": 1046,
+ "survivors": 917,
+ "losses": 0,
+ "injured": 46,
+ "lightly_injured": 83,
+ "kills": 300
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 242,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 85,
+ "lightly_injured": 157,
+ "kills": 2
+ },
+ "lancer_t6": {
+ "troops": 280,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 98,
+ "lightly_injured": 182,
+ "kills": 14
+ },
+ "marksman_t6": {
+ "troops": 332,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 117,
+ "lightly_injured": 215,
+ "kills": 30
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 398,
+ "survivors": 269,
+ "losses": 0,
+ "injured": 46,
+ "lightly_injured": 83,
+ "kills": 41
+ },
+ "lancer_t6": {
+ "troops": 277,
+ "survivors": 277,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 151
+ },
+ "marksman_t6": {
+ "troops": 371,
+ "survivors": 371,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 108
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 3,
+ "displayed_kills": 1
+ }
+ }
+ },
+ "defender": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 2,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 5,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_c2_twenty/gatot_renee_gwen_vs_sergey_sonya_wayne.json
===================================================================
--- testcases/emulator_verified/gwen_c2_twenty/gatot_renee_gwen_vs_sergey_sonya_wayne.json prev run
+++ testcases/emulator_verified/gwen_c2_twenty/gatot_renee_gwen_vs_sergey_sonya_wayne.json this run
@@ -0,0 +1,231 @@
+[
+ {
+ "test_id": "gatot_renee_gwen_vs_sergey_sonya_wayne",
+ "description": "C2 battle report 2734693171109665",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gatot": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 349,
+ "lancer_t6": 387,
+ "marksman_t6": 238
+ },
+ "stats": {
+ "infantry": {
+ "attack": 627.31,
+ "defense": 619.86,
+ "lethality": 230.66,
+ "health": 281.02
+ },
+ "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": {
+ "Sergey": {
+ "skill_1": 4,
+ "skill_2": 4
+ },
+ "Sonya": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ },
+ "Wayne": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 304,
+ "lancer_t6": 384,
+ "marksman_t6": 277
+ },
+ "stats": {
+ "infantry": {
+ "attack": 409.72,
+ "defense": 402.72,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 529.96,
+ "defense": 534.21,
+ "lethality": 323.16,
+ "health": 285.53
+ },
+ "marksman": {
+ "attack": 421.39,
+ "defense": 427.39,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-21T22:31:00.000Z",
+ "randomseed": 1790029861,
+ "attacker": 744,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171109665",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-21T22-31-28.112Z-bf1315a8-7d67-484e-bc96-42abbb15d173.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 974,
+ "survivors": 744,
+ "losses": 0,
+ "injured": 81,
+ "lightly_injured": 149,
+ "kills": 339
+ },
+ "defender": {
+ "troops": 965,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 339,
+ "lightly_injured": 626,
+ "kills": 81
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 349,
+ "survivors": 161,
+ "losses": 0,
+ "injured": 66,
+ "lightly_injured": 122,
+ "kills": 40
+ },
+ "lancer_t6": {
+ "troops": 387,
+ "survivors": 367,
+ "losses": 0,
+ "injured": 7,
+ "lightly_injured": 13,
+ "kills": 207
+ },
+ "marksman_t6": {
+ "troops": 238,
+ "survivors": 216,
+ "losses": 0,
+ "injured": 8,
+ "lightly_injured": 14,
+ "kills": 92
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 304,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 107,
+ "lightly_injured": 197,
+ "kills": 0
+ },
+ "lancer_t6": {
+ "troops": 384,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 135,
+ "lightly_injured": 249,
+ "kills": 29
+ },
+ "marksman_t6": {
+ "troops": 277,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 97,
+ "lightly_injured": 180,
+ "kills": 52
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 13,
+ "displayed_kills": 7
+ }
+ },
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": 26,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 15,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 13,
+ "displayed_kills": 2
+ },
+ "Torrential Impact": {
+ "triggered": 5,
+ "displayed_kills": 3
+ }
+ },
+ "Wayne": {
+ "Thunder Strike": {
+ "triggered": 17,
+ "displayed_kills": 5
+ },
+ "Roundabout Hit": {
+ "triggered": 15,
+ "displayed_kills": 14
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_c2_twenty/gatot_sonya_gwen_vs_edith_gordon_bradley.json
===================================================================
--- testcases/emulator_verified/gwen_c2_twenty/gatot_sonya_gwen_vs_edith_gordon_bradley.json prev run
+++ testcases/emulator_verified/gwen_c2_twenty/gatot_sonya_gwen_vs_edith_gordon_bradley.json this run
@@ -0,0 +1,237 @@
+[
+ {
+ "test_id": "gatot_sonya_gwen_vs_edith_gordon_bradley",
+ "description": "C2 battle report 2734693171109854",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gatot": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Sonya": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 268,
+ "lancer_t6": 306,
+ "marksman_t6": 358
+ },
+ "stats": {
+ "infantry": {
+ "attack": 670.13,
+ "defense": 663.13,
+ "lethality": 316.11,
+ "health": 338.06
+ },
+ "lancer": {
+ "attack": 529.96,
+ "defense": 534.21,
+ "lethality": 323.16,
+ "health": 285.53
+ },
+ "marksman": {
+ "attack": 434.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Edith": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ },
+ "Gordon": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ },
+ "Bradley": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 1
+ }
+ },
+ "troops": {
+ "infantry_t6": 223,
+ "lancer_t6": 303,
+ "marksman_t6": 397
+ },
+ "stats": {
+ "infantry": {
+ "attack": 401.82,
+ "defense": 394.37,
+ "lethality": 193.33,
+ "health": 198.83
+ },
+ "lancer": {
+ "attack": 364.09,
+ "defense": 363.39,
+ "lethality": 191.83,
+ "health": 190.89
+ },
+ "marksman": {
+ "attack": 681.96,
+ "defense": 670.76,
+ "lethality": 322.27,
+ "health": 273.25
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-21T22:51:47.000Z",
+ "randomseed": 1790031108,
+ "attacker": 743,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171109854",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-21T22-52-04.127Z-975a0b21-a192-49d6-8c21-cc9b20548611.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 932,
+ "survivors": 743,
+ "losses": 0,
+ "injured": 67,
+ "lightly_injured": 122,
+ "kills": 325
+ },
+ "defender": {
+ "troops": 923,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 325,
+ "lightly_injured": 598,
+ "kills": 67
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 268,
+ "survivors": 79,
+ "losses": 0,
+ "injured": 67,
+ "lightly_injured": 122,
+ "kills": 39
+ },
+ "lancer_t6": {
+ "troops": 306,
+ "survivors": 306,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 162
+ },
+ "marksman_t6": {
+ "troops": 358,
+ "survivors": 358,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 124
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 223,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 79,
+ "lightly_injured": 144,
+ "kills": 0
+ },
+ "lancer_t6": {
+ "troops": 303,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 107,
+ "lightly_injured": 196,
+ "kills": 7
+ },
+ "marksman_t6": {
+ "troops": 397,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 139,
+ "lightly_injured": 258,
+ "kills": 60
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 10,
+ "displayed_kills": 8
+ },
+ "Torrential Impact": {
+ "triggered": 4,
+ "displayed_kills": 16
+ }
+ },
+ "Gwen": {
+ "Blastmaster": {
+ "triggered": 3,
+ "displayed_kills": 4
+ },
+ "Air Dominance": {
+ "triggered": 9,
+ "displayed_kills": 8
+ }
+ },
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": 21,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Gordon": {
+ "Venom Infusion": {
+ "triggered": 6,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 7,
+ "displayed_kills": null
+ }
+ },
+ "Bradley": {
+ "Tactical Assistance": {
+ "triggered": 5,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_c2_twenty/gwen_vs_ahmose.json
===================================================================
--- testcases/emulator_verified/gwen_c2_twenty/gwen_vs_ahmose.json prev run
+++ testcases/emulator_verified/gwen_c2_twenty/gwen_vs_ahmose.json this run
@@ -0,0 +1,188 @@
+[
+ {
+ "test_id": "gwen_vs_ahmose",
+ "description": "C2 battle report 2734693171109662",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 341,
+ "lancer_t6": 379,
+ "marksman_t6": 230
+ },
+ "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": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 296,
+ "lancer_t6": 376,
+ "marksman_t6": 269
+ },
+ "stats": {
+ "infantry": {
+ "attack": 345.57,
+ "defense": 338.12,
+ "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-21T22:30:46.000Z",
+ "randomseed": 1790029847,
+ "attacker": 738,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171109662",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-21T22-31-03.121Z-46ea4b77-3e2d-48be-b483-c9d514d772f4.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 950,
+ "survivors": 738,
+ "losses": 0,
+ "injured": 75,
+ "lightly_injured": 137,
+ "kills": 331
+ },
+ "defender": {
+ "troops": 941,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 331,
+ "lightly_injured": 610,
+ "kills": 75
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 341,
+ "survivors": 129,
+ "losses": 0,
+ "injured": 75,
+ "lightly_injured": 137,
+ "kills": 32
+ },
+ "lancer_t6": {
+ "troops": 379,
+ "survivors": 379,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 126
+ },
+ "marksman_t6": {
+ "troops": 230,
+ "survivors": 230,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 173
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 296,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 104,
+ "lightly_injured": 192,
+ "kills": 6
+ },
+ "lancer_t6": {
+ "troops": 376,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 132,
+ "lightly_injured": 244,
+ "kills": 28
+ },
+ "marksman_t6": {
+ "troops": 269,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 95,
+ "lightly_injured": 174,
+ "kills": 41
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 11,
+ "displayed_kills": 8
+ },
+ "Blastmaster": {
+ "triggered": 4,
+ "displayed_kills": 12
+ }
+ }
+ },
+ "defender": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 3,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_c2_twenty/gwen_vs_bradley.json
===================================================================
--- testcases/emulator_verified/gwen_c2_twenty/gwen_vs_bradley.json prev run
+++ testcases/emulator_verified/gwen_c2_twenty/gwen_vs_bradley.json this run
@@ -0,0 +1,189 @@
+[
+ {
+ "test_id": "gwen_vs_bradley",
+ "description": "C2 battle report 2734693171109763",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 375,
+ "lancer_t6": 212,
+ "marksman_t6": 264
+ },
+ "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": {
+ "Bradley": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 1
+ }
+ },
+ "troops": {
+ "infantry_t6": 330,
+ "lancer_t6": 209,
+ "marksman_t6": 303
+ },
+ "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": 681.96,
+ "defense": 670.76,
+ "lethality": 322.27,
+ "health": 273.25
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-21T22:43:05.000Z",
+ "randomseed": 1790030586,
+ "attacker": 0,
+ "defender": 502,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171109763",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-21T22-43-22.124Z-0a665ace-25f4-423b-ad5f-f952b37f55f0.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 851,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 300,
+ "lightly_injured": 551,
+ "kills": 121
+ },
+ "defender": {
+ "troops": 842,
+ "survivors": 502,
+ "losses": 0,
+ "injured": 121,
+ "lightly_injured": 219,
+ "kills": 300
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 375,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 132,
+ "lightly_injured": 243,
+ "kills": 9
+ },
+ "lancer_t6": {
+ "troops": 212,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 75,
+ "lightly_injured": 137,
+ "kills": 26
+ },
+ "marksman_t6": {
+ "troops": 264,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 93,
+ "lightly_injured": 171,
+ "kills": 86
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 330,
+ "survivors": 6,
+ "losses": 0,
+ "injured": 114,
+ "lightly_injured": 210,
+ "kills": 10
+ },
+ "lancer_t6": {
+ "troops": 209,
+ "survivors": 197,
+ "losses": 0,
+ "injured": 5,
+ "lightly_injured": 7,
+ "kills": 45
+ },
+ "marksman_t6": {
+ "troops": 303,
+ "survivors": 299,
+ "losses": 0,
+ "injured": 2,
+ "lightly_injured": 2,
+ "kills": 245
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 5,
+ "displayed_kills": 2
+ },
+ "Blastmaster": {
+ "triggered": 3,
+ "displayed_kills": 7
+ }
+ }
+ },
+ "defender": {
+ "Bradley": {
+ "Tactical Assistance": {
+ "triggered": 4,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_c2_twenty/gwen_vs_flint_jessie_wayne.json
===================================================================
--- testcases/emulator_verified/gwen_c2_twenty/gwen_vs_flint_jessie_wayne.json prev run
+++ testcases/emulator_verified/gwen_c2_twenty/gwen_vs_flint_jessie_wayne.json this run
@@ -0,0 +1,197 @@
+[
+ {
+ "test_id": "gwen_vs_flint_jessie_wayne",
+ "description": "C2 battle report 2734693171109628",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 208,
+ "lancer_t6": 246,
+ "marksman_t6": 298
+ },
+ "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": {
+ "Flint": {
+ "skill_1": 2,
+ "skill_2": 2,
+ "skill_3": 1,
+ "skill_4": 0
+ },
+ "Jessie": {
+ "skill_1": 5,
+ "skill_2": 5
+ },
+ "Wayne": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 364,
+ "lancer_t6": 243,
+ "marksman_t6": 337
+ },
+ "stats": {
+ "infantry": {
+ "attack": 398.37,
+ "defense": 391.37,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 391.69,
+ "defense": 395.94,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 421.39,
+ "defense": 427.39,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-21T22:26:45.000Z",
+ "randomseed": 1790029606,
+ "attacker": 0,
+ "defender": 875,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171109628",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-21T22-27-13.118Z-c477820f-ed56-4d7c-a7cd-93141be5ae8a.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 752,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 265,
+ "lightly_injured": 487,
+ "kills": 25
+ },
+ "defender": {
+ "troops": 944,
+ "survivors": 875,
+ "losses": 0,
+ "injured": 25,
+ "lightly_injured": 44,
+ "kills": 265
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 208,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 73,
+ "lightly_injured": 135,
+ "kills": 0
+ },
+ "lancer_t6": {
+ "troops": 246,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 87,
+ "lightly_injured": 159,
+ "kills": 5
+ },
+ "marksman_t6": {
+ "troops": 298,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 193,
+ "kills": 20
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 364,
+ "survivors": 295,
+ "losses": 0,
+ "injured": 25,
+ "lightly_injured": 44,
+ "kills": 50
+ },
+ "lancer_t6": {
+ "troops": 243,
+ "survivors": 243,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 87
+ },
+ "marksman_t6": {
+ "troops": 337,
+ "survivors": 337,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 128
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 3,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Wayne": {
+ "Roundabout Hit": {
+ "triggered": 5,
+ "displayed_kills": 9
+ },
+ "Thunder Strike": {
+ "triggered": 6,
+ "displayed_kills": 12
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_c2_twenty/gwen_vs_gatot.json
===================================================================
--- testcases/emulator_verified/gwen_c2_twenty/gwen_vs_gatot.json prev run
+++ testcases/emulator_verified/gwen_c2_twenty/gwen_vs_gatot.json this run
@@ -0,0 +1,189 @@
+[
+ {
+ "test_id": "gwen_vs_gatot",
+ "description": "C2 battle report 2734693171109689",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 388,
+ "lancer_t6": 225,
+ "marksman_t6": 277
+ },
+ "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": {
+ "Gatot": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 343,
+ "lancer_t6": 222,
+ "marksman_t6": 316
+ },
+ "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-21T22:32:40.000Z",
+ "randomseed": 1790029961,
+ "attacker": 0,
+ "defender": 660,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171109689",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-21T22-32-57.117Z-12de6902-a18b-40f7-b312-d7f405b624ae.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 890,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 312,
+ "lightly_injured": 578,
+ "kills": 78
+ },
+ "defender": {
+ "troops": 881,
+ "survivors": 660,
+ "losses": 0,
+ "injured": 78,
+ "lightly_injured": 143,
+ "kills": 312
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 388,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 136,
+ "lightly_injured": 252,
+ "kills": 1
+ },
+ "lancer_t6": {
+ "troops": 225,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 79,
+ "lightly_injured": 146,
+ "kills": 0
+ },
+ "marksman_t6": {
+ "troops": 277,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 97,
+ "lightly_injured": 180,
+ "kills": 77
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 343,
+ "survivors": 201,
+ "losses": 0,
+ "injured": 50,
+ "lightly_injured": 92,
+ "kills": 84
+ },
+ "lancer_t6": {
+ "troops": 222,
+ "survivors": 188,
+ "losses": 0,
+ "injured": 12,
+ "lightly_injured": 22,
+ "kills": 85
+ },
+ "marksman_t6": {
+ "troops": 316,
+ "survivors": 271,
+ "losses": 0,
+ "injured": 16,
+ "lightly_injured": 29,
+ "kills": 143
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 20,
+ "displayed_kills": 2
+ },
+ "Blastmaster": {
+ "triggered": 10,
+ "displayed_kills": 28
+ }
+ }
+ },
+ "defender": {
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": 52,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_c2_twenty/gwen_vs_hendrik.json
===================================================================
--- testcases/emulator_verified/gwen_c2_twenty/gwen_vs_hendrik.json prev run
+++ testcases/emulator_verified/gwen_c2_twenty/gwen_vs_hendrik.json this run
@@ -0,0 +1,193 @@
+[
+ {
+ "test_id": "gwen_vs_hendrik",
+ "description": "C2 battle report 2734693171109754",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 328,
+ "lancer_t6": 366,
+ "marksman_t6": 217
+ },
+ "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": 283,
+ "lancer_t6": 363,
+ "marksman_t6": 256
+ },
+ "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-21T22:40:13.000Z",
+ "randomseed": 1790030414,
+ "attacker": 220,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171109754",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-21T22-40-30.115Z-8677dc63-aba5-4971-95a9-6f610a9ff354.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 911,
+ "survivors": 220,
+ "losses": 0,
+ "injured": 243,
+ "lightly_injured": 448,
+ "kills": 318
+ },
+ "defender": {
+ "troops": 902,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 318,
+ "lightly_injured": 584,
+ "kills": 243
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 328,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 115,
+ "lightly_injured": 213,
+ "kills": 15
+ },
+ "lancer_t6": {
+ "troops": 366,
+ "survivors": 71,
+ "losses": 0,
+ "injured": 104,
+ "lightly_injured": 191,
+ "kills": 117
+ },
+ "marksman_t6": {
+ "troops": 217,
+ "survivors": 149,
+ "losses": 0,
+ "injured": 24,
+ "lightly_injured": 44,
+ "kills": 186
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 283,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 100,
+ "lightly_injured": 183,
+ "kills": 4
+ },
+ "lancer_t6": {
+ "troops": 363,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 128,
+ "lightly_injured": 235,
+ "kills": 29
+ },
+ "marksman_t6": {
+ "troops": 256,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 90,
+ "lightly_injured": 166,
+ "kills": 210
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 11,
+ "displayed_kills": 8
+ },
+ "Blastmaster": {
+ "triggered": 5,
+ "displayed_kills": 8
+ }
+ }
+ },
+ "defender": {
+ "Hendrik": {
+ "Dagon's Heir": {
+ "triggered": 9,
+ "displayed_kills": 54
+ },
+ "Armor of Barnacles": {
+ "triggered": 7,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_c2_twenty/gwen_vs_patrick.json
===================================================================
--- testcases/emulator_verified/gwen_c2_twenty/gwen_vs_patrick.json prev run
+++ testcases/emulator_verified/gwen_c2_twenty/gwen_vs_patrick.json this run
@@ -0,0 +1,180 @@
+[
+ {
+ "test_id": "gwen_vs_patrick",
+ "description": "C2 battle report 2734693171109637",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 294,
+ "lancer_t6": 332,
+ "marksman_t6": 384
+ },
+ "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": {
+ "Patrick": {
+ "skill_1": 4,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 249,
+ "lancer_t6": 329,
+ "marksman_t6": 222
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 293.2,
+ "defense": 292.5,
+ "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-21T22:28:45.000Z",
+ "randomseed": 1790029726,
+ "attacker": 812,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171109637",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-21T22-29-02.118Z-a4a0b05f-df6f-4711-a8e4-42f45ed04065.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1010,
+ "survivors": 812,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 128,
+ "kills": 282
+ },
+ "defender": {
+ "troops": 800,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 282,
+ "lightly_injured": 518,
+ "kills": 70
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 294,
+ "survivors": 96,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 128,
+ "kills": 22
+ },
+ "lancer_t6": {
+ "troops": 332,
+ "survivors": 332,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 84
+ },
+ "marksman_t6": {
+ "troops": 384,
+ "survivors": 384,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 176
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 249,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 161,
+ "kills": 2
+ },
+ "lancer_t6": {
+ "troops": 329,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 116,
+ "lightly_injured": 213,
+ "kills": 32
+ },
+ "marksman_t6": {
+ "troops": 222,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 78,
+ "lightly_injured": 144,
+ "kills": 36
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 9,
+ "displayed_kills": 3
+ },
+ "Blastmaster": {
+ "triggered": 4,
+ "displayed_kills": 11
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_c2_twenty/gwen_vs_renee.json
===================================================================
--- testcases/emulator_verified/gwen_c2_twenty/gwen_vs_renee.json prev run
+++ testcases/emulator_verified/gwen_c2_twenty/gwen_vs_renee.json this run
@@ -0,0 +1,189 @@
+[
+ {
+ "test_id": "gwen_vs_renee",
+ "description": "C2 battle report 2734693171109713",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 234,
+ "lancer_t6": 272,
+ "marksman_t6": 324
+ },
+ "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": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 390,
+ "lancer_t6": 269,
+ "marksman_t6": 363
+ },
+ "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": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-21T22:34:34.000Z",
+ "randomseed": 1790030075,
+ "attacker": 0,
+ "defender": 586,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171109713",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-21T22-34-51.108Z-5e78224f-002d-4ecb-afa1-f1e7f430c91d.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 830,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 292,
+ "lightly_injured": 538,
+ "kills": 154
+ },
+ "defender": {
+ "troops": 1022,
+ "survivors": 586,
+ "losses": 0,
+ "injured": 154,
+ "lightly_injured": 282,
+ "kills": 292
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 234,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 82,
+ "lightly_injured": 152,
+ "kills": 5
+ },
+ "lancer_t6": {
+ "troops": 272,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 96,
+ "lightly_injured": 176,
+ "kills": 33
+ },
+ "marksman_t6": {
+ "troops": 324,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 114,
+ "lightly_injured": 210,
+ "kills": 116
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 390,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 137,
+ "lightly_injured": 253,
+ "kills": 14
+ },
+ "lancer_t6": {
+ "troops": 269,
+ "survivors": 243,
+ "losses": 0,
+ "injured": 10,
+ "lightly_injured": 16,
+ "kills": 184
+ },
+ "marksman_t6": {
+ "troops": 363,
+ "survivors": 343,
+ "losses": 0,
+ "injured": 7,
+ "lightly_injured": 13,
+ "kills": 94
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 6,
+ "displayed_kills": 3
+ },
+ "Blastmaster": {
+ "triggered": 4,
+ "displayed_kills": 11
+ }
+ }
+ },
+ "defender": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 10,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_c2_twenty/gwen_vs_sergey.json
===================================================================
--- testcases/emulator_verified/gwen_c2_twenty/gwen_vs_sergey.json prev run
+++ testcases/emulator_verified/gwen_c2_twenty/gwen_vs_sergey.json this run
@@ -0,0 +1,180 @@
+[
+ {
+ "test_id": "gwen_vs_sergey",
+ "description": "C2 battle report 2734693171109624",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 247,
+ "lancer_t6": 285,
+ "marksman_t6": 337
+ },
+ "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": {
+ "Sergey": {
+ "skill_1": 2,
+ "skill_2": 2
+ }
+ },
+ "troops": {
+ "infantry_t6": 202,
+ "lancer_t6": 282,
+ "marksman_t6": 376
+ },
+ "stats": {
+ "infantry": {
+ "attack": 346.14,
+ "defense": 338.69,
+ "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-21T22:26:31.000Z",
+ "randomseed": 1790029592,
+ "attacker": 699,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171109624",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-21T22-26-48.114Z-26005942-440c-4725-83ba-0cce79a5bf1d.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 869,
+ "survivors": 699,
+ "losses": 0,
+ "injured": 60,
+ "lightly_injured": 110,
+ "kills": 302
+ },
+ "defender": {
+ "troops": 860,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 302,
+ "lightly_injured": 558,
+ "kills": 60
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 247,
+ "survivors": 77,
+ "losses": 0,
+ "injured": 60,
+ "lightly_injured": 110,
+ "kills": 18
+ },
+ "lancer_t6": {
+ "troops": 285,
+ "survivors": 285,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 93
+ },
+ "marksman_t6": {
+ "troops": 337,
+ "survivors": 337,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 191
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 202,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 71,
+ "lightly_injured": 131,
+ "kills": 4
+ },
+ "lancer_t6": {
+ "troops": 282,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 99,
+ "lightly_injured": 183,
+ "kills": 17
+ },
+ "marksman_t6": {
+ "troops": 376,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 132,
+ "lightly_injured": 244,
+ "kills": 39
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 9,
+ "displayed_kills": 7
+ },
+ "Blastmaster": {
+ "triggered": 4,
+ "displayed_kills": 10
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_c2_twenty/gwen_vs_sonya.json
===================================================================
--- testcases/emulator_verified/gwen_c2_twenty/gwen_vs_sonya.json prev run
+++ testcases/emulator_verified/gwen_c2_twenty/gwen_vs_sonya.json this run
@@ -0,0 +1,188 @@
+[
+ {
+ "test_id": "gwen_vs_sonya",
+ "description": "C2 battle report 2734693171109740",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 281,
+ "lancer_t6": 319,
+ "marksman_t6": 371
+ },
+ "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": {
+ "Sonya": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 236,
+ "lancer_t6": 316,
+ "marksman_t6": 209
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 369.5,
+ "defense": 368.8,
+ "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-21T22:37:30.000Z",
+ "randomseed": 1790030251,
+ "attacker": 808,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171109740",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-21T22-37-47.115Z-db520607-beb7-40e6-83c8-50a7c52d731b.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 971,
+ "survivors": 808,
+ "losses": 0,
+ "injured": 58,
+ "lightly_injured": 105,
+ "kills": 268
+ },
+ "defender": {
+ "troops": 761,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 268,
+ "lightly_injured": 493,
+ "kills": 58
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 281,
+ "survivors": 118,
+ "losses": 0,
+ "injured": 58,
+ "lightly_injured": 105,
+ "kills": 21
+ },
+ "lancer_t6": {
+ "troops": 319,
+ "survivors": 319,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 83
+ },
+ "marksman_t6": {
+ "troops": 371,
+ "survivors": 371,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 164
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 236,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 83,
+ "lightly_injured": 153,
+ "kills": 2
+ },
+ "lancer_t6": {
+ "troops": 316,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 111,
+ "lightly_injured": 205,
+ "kills": 30
+ },
+ "marksman_t6": {
+ "troops": 209,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 74,
+ "lightly_injured": 135,
+ "kills": 26
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 9,
+ "displayed_kills": 8
+ },
+ "Blastmaster": {
+ "triggered": 3,
+ "displayed_kills": 10
+ }
+ }
+ },
+ "defender": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 8,
+ "displayed_kills": 2
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_c2_twenty/gwen_vs_wu_ming_sonya_jasser.json
===================================================================
--- testcases/emulator_verified/gwen_c2_twenty/gwen_vs_wu_ming_sonya_jasser.json prev run
+++ testcases/emulator_verified/gwen_c2_twenty/gwen_vs_wu_ming_sonya_jasser.json this run
@@ -0,0 +1,198 @@
+[
+ {
+ "test_id": "gwen_vs_wu_ming_sonya_jasser",
+ "description": "C2 battle report 2734693171109640",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 255,
+ "lancer_t6": 293,
+ "marksman_t6": 345
+ },
+ "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": {
+ "Wu Ming": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 2,
+ "skill_4": 0
+ },
+ "Sonya": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ },
+ "Jasser": {
+ "skill_1": 4,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t6": 210,
+ "lancer_t6": 290,
+ "marksman_t6": 384
+ },
+ "stats": {
+ "infantry": {
+ "attack": 508.98,
+ "defense": 501.98,
+ "lethality": 250.71,
+ "health": 274.79
+ },
+ "lancer": {
+ "attack": 529.96,
+ "defense": 534.21,
+ "lethality": 323.16,
+ "health": 285.53
+ },
+ "marksman": {
+ "attack": 354.37,
+ "defense": 360.37,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-21T22:28:59.000Z",
+ "randomseed": 1790029740,
+ "attacker": 0,
+ "defender": 822,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171109640",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-21T22-29-27.112Z-fbed3d97-9e4a-4e31-be38-4278c8987343.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 893,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 314,
+ "lightly_injured": 579,
+ "kills": 22
+ },
+ "defender": {
+ "troops": 884,
+ "survivors": 822,
+ "losses": 0,
+ "injured": 22,
+ "lightly_injured": 40,
+ "kills": 314
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 255,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 90,
+ "lightly_injured": 165,
+ "kills": 0
+ },
+ "lancer_t6": {
+ "troops": 293,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 103,
+ "lightly_injured": 190,
+ "kills": 5
+ },
+ "marksman_t6": {
+ "troops": 345,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 121,
+ "lightly_injured": 224,
+ "kills": 17
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 210,
+ "survivors": 148,
+ "losses": 0,
+ "injured": 22,
+ "lightly_injured": 40,
+ "kills": 33
+ },
+ "lancer_t6": {
+ "troops": 290,
+ "survivors": 290,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 165
+ },
+ "marksman_t6": {
+ "troops": 384,
+ "survivors": 384,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 116
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 2,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 5,
+ "displayed_kills": 11
+ },
+ "Torrential Impact": {
+ "triggered": 2,
+ "displayed_kills": 17
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_c2_twenty/sergey_jessie_hendrik_vs_gwen.json
===================================================================
--- testcases/emulator_verified/gwen_c2_twenty/sergey_jessie_hendrik_vs_gwen.json prev run
+++ testcases/emulator_verified/gwen_c2_twenty/sergey_jessie_hendrik_vs_gwen.json this run
@@ -0,0 +1,201 @@
+[
+ {
+ "test_id": "sergey_jessie_hendrik_vs_gwen",
+ "description": "C2 battle report 2734693171110008",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Sergey": {
+ "skill_1": 2,
+ "skill_2": 2
+ },
+ "Jessie": {
+ "skill_1": 1,
+ "skill_2": 1
+ },
+ "Hendrik": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 289,
+ "lancer_t6": 327,
+ "marksman_t6": 379
+ },
+ "stats": {
+ "infantry": {
+ "attack": 346.14,
+ "defense": 338.69,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "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": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 244,
+ "lancer_t6": 324,
+ "marksman_t6": 217
+ },
+ "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": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-21T22:59:54.000Z",
+ "randomseed": 1790031595,
+ "attacker": 779,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171110008",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-21T23-00-22.116Z-d2388695-f2f3-405e-ab4b-154e91116689.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 995,
+ "survivors": 779,
+ "losses": 0,
+ "injured": 77,
+ "lightly_injured": 139,
+ "kills": 276
+ },
+ "defender": {
+ "troops": 785,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 276,
+ "lightly_injured": 509,
+ "kills": 77
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 289,
+ "survivors": 84,
+ "losses": 0,
+ "injured": 72,
+ "lightly_injured": 133,
+ "kills": 16
+ },
+ "lancer_t6": {
+ "troops": 327,
+ "survivors": 321,
+ "losses": 0,
+ "injured": 3,
+ "lightly_injured": 3,
+ "kills": 67
+ },
+ "marksman_t6": {
+ "troops": 379,
+ "survivors": 374,
+ "losses": 0,
+ "injured": 2,
+ "lightly_injured": 3,
+ "kills": 193
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 244,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 86,
+ "lightly_injured": 158,
+ "kills": 4
+ },
+ "lancer_t6": {
+ "troops": 324,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 114,
+ "lightly_injured": 210,
+ "kills": 22
+ },
+ "marksman_t6": {
+ "troops": 217,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 76,
+ "lightly_injured": 141,
+ "kills": 51
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Hendrik": {
+ "Dagon's Heir": {
+ "triggered": 6,
+ "displayed_kills": 42
+ },
+ "Armor of Barnacles": {
+ "triggered": 4,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 6,
+ "displayed_kills": 1
+ },
+ "Blastmaster": {
+ "triggered": 3,
+ "displayed_kills": 4
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_c2_twenty/wu_ming_gordon_gwen_vs_gatot_jessie_hendrik.json
===================================================================
--- testcases/emulator_verified/gwen_c2_twenty/wu_ming_gordon_gwen_vs_gatot_jessie_hendrik.json prev run
+++ testcases/emulator_verified/gwen_c2_twenty/wu_ming_gordon_gwen_vs_gatot_jessie_hendrik.json this run
@@ -0,0 +1,235 @@
+[
+ {
+ "test_id": "wu_ming_gordon_gwen_vs_gatot_jessie_hendrik",
+ "description": "C2 battle report 2734693171109873",
+ "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": 508.98,
+ "defense": 501.98,
+ "lethality": 250.71,
+ "health": 274.79
+ },
+ "lancer": {
+ "attack": 488.62,
+ "defense": 492.87,
+ "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
+ },
+ "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-21T22:55:11.000Z",
+ "randomseed": 1790031312,
+ "attacker": 0,
+ "defender": 595,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171109873",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-21T22-55-28.118Z-9f15ffab-841a-41b7-8e4c-b895076ab9aa.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 872,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 307,
+ "lightly_injured": 565,
+ "kills": 96
+ },
+ "defender": {
+ "troops": 863,
+ "survivors": 595,
+ "losses": 0,
+ "injured": 96,
+ "lightly_injured": 172,
+ "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": 35
+ },
+ "marksman_t6": {
+ "troops": 204,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 72,
+ "lightly_injured": 132,
+ "kills": 61
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 270,
+ "survivors": 35,
+ "losses": 0,
+ "injured": 83,
+ "lightly_injured": 152,
+ "kills": 29
+ },
+ "lancer_t6": {
+ "troops": 350,
+ "survivors": 332,
+ "losses": 0,
+ "injured": 7,
+ "lightly_injured": 11,
+ "kills": 80
+ },
+ "marksman_t6": {
+ "troops": 243,
+ "survivors": 228,
+ "losses": 0,
+ "injured": 6,
+ "lightly_injured": 9,
+ "kills": 198
+ }
+ }
+ },
+ "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": 60
+ },
+ "Armor of Barnacles": {
+ "triggered": 10,
+ "displayed_kills": null
+ }
+ },
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": 43,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_c2_twenty/wu_ming_sonya_bradley_vs_gatot_patrick_gwen.json
===================================================================
--- testcases/emulator_verified/gwen_c2_twenty/wu_ming_sonya_bradley_vs_gatot_patrick_gwen.json prev run
+++ testcases/emulator_verified/gwen_c2_twenty/wu_ming_sonya_bradley_vs_gatot_patrick_gwen.json this run
@@ -0,0 +1,222 @@
+[
+ {
+ "test_id": "wu_ming_sonya_bradley_vs_gatot_patrick_gwen",
+ "description": "C2 battle report 2734693171110114",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Wu Ming": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Sonya": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ },
+ "Bradley": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 1
+ }
+ },
+ "troops": {
+ "infantry_t6": 336,
+ "lancer_t6": 374,
+ "marksman_t6": 225
+ },
+ "stats": {
+ "infantry": {
+ "attack": 453.71,
+ "defense": 446.26,
+ "lethality": 223.57,
+ "health": 274.97
+ },
+ "lancer": {
+ "attack": 369.5,
+ "defense": 368.8,
+ "lethality": 187,
+ "health": 186.06
+ },
+ "marksman": {
+ "attack": 681.96,
+ "defense": 670.76,
+ "lethality": 322.27,
+ "health": 273.25
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gatot": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Patrick": {
+ "skill_1": 3,
+ "skill_2": 2
+ },
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 291,
+ "lancer_t6": 371,
+ "marksman_t6": 264
+ },
+ "stats": {
+ "infantry": {
+ "attack": 670.13,
+ "defense": 663.13,
+ "lethality": 316.11,
+ "health": 338.06
+ },
+ "lancer": {
+ "attack": 391.69,
+ "defense": 395.94,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 434.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-21T23:12:03.000Z",
+ "randomseed": 1790032324,
+ "attacker": 0,
+ "defender": 338,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171110114",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-21T23-12-31.120Z-d5acb885-462b-4622-92b1-a617896c9ae8.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 935,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 328,
+ "lightly_injured": 607,
+ "kills": 206
+ },
+ "defender": {
+ "troops": 926,
+ "survivors": 338,
+ "losses": 0,
+ "injured": 206,
+ "lightly_injured": 382,
+ "kills": 328
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 336,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 118,
+ "lightly_injured": 218,
+ "kills": 0
+ },
+ "lancer_t6": {
+ "troops": 374,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 131,
+ "lightly_injured": 243,
+ "kills": 30
+ },
+ "marksman_t6": {
+ "troops": 225,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 79,
+ "lightly_injured": 146,
+ "kills": 176
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 291,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 102,
+ "lightly_injured": 189,
+ "kills": 25
+ },
+ "lancer_t6": {
+ "troops": 371,
+ "survivors": 74,
+ "losses": 0,
+ "injured": 104,
+ "lightly_injured": 193,
+ "kills": 131
+ },
+ "marksman_t6": {
+ "troops": 264,
+ "survivors": 264,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 172
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 15,
+ "displayed_kills": 2
+ }
+ },
+ "Bradley": {
+ "Tactical Assistance": {
+ "triggered": 9,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 15,
+ "displayed_kills": 8
+ },
+ "Blastmaster": {
+ "triggered": 7,
+ "displayed_kills": 9
+ }
+ },
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": 29,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_c2_twenty/wu_ming_sonya_gwen_vs_edith_gordon_bradley.json
===================================================================
--- testcases/emulator_verified/gwen_c2_twenty/wu_ming_sonya_gwen_vs_edith_gordon_bradley.json prev run
+++ testcases/emulator_verified/gwen_c2_twenty/wu_ming_sonya_gwen_vs_edith_gordon_bradley.json this run
@@ -0,0 +1,227 @@
+[
+ {
+ "test_id": "wu_ming_sonya_gwen_vs_edith_gordon_bradley",
+ "description": "C2 battle report 2734693171109718",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Wu Ming": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Sonya": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ },
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 396,
+ "lancer_t6": 233,
+ "marksman_t6": 285
+ },
+ "stats": {
+ "infantry": {
+ "attack": 453.71,
+ "defense": 446.26,
+ "lethality": 223.57,
+ "health": 274.97
+ },
+ "lancer": {
+ "attack": 369.5,
+ "defense": 368.8,
+ "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": {
+ "Edith": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Gordon": {
+ "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": {
+ "infantry_t6": 351,
+ "lancer_t6": 230,
+ "marksman_t6": 324
+ },
+ "stats": {
+ "infantry": {
+ "attack": 521.6,
+ "defense": 514.6,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 488.62,
+ "defense": 492.87,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 734.42,
+ "defense": 740.42,
+ "lethality": 375.19,
+ "health": 331.96
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-21T22:34:48.000Z",
+ "randomseed": 1790030089,
+ "attacker": 0,
+ "defender": 784,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171109718",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-21T22-35-16.112Z-492cca79-3c4c-4812-9c99-8232733151c2.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 914,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 321,
+ "lightly_injured": 593,
+ "kills": 43
+ },
+ "defender": {
+ "troops": 905,
+ "survivors": 784,
+ "losses": 0,
+ "injured": 43,
+ "lightly_injured": 78,
+ "kills": 321
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 396,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 139,
+ "lightly_injured": 257,
+ "kills": 5
+ },
+ "lancer_t6": {
+ "troops": 233,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 82,
+ "lightly_injured": 151,
+ "kills": 16
+ },
+ "marksman_t6": {
+ "troops": 285,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 100,
+ "lightly_injured": 185,
+ "kills": 22
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 351,
+ "survivors": 230,
+ "losses": 0,
+ "injured": 43,
+ "lightly_injured": 78,
+ "kills": 33
+ },
+ "lancer_t6": {
+ "troops": 230,
+ "survivors": 230,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 104
+ },
+ "marksman_t6": {
+ "troops": 324,
+ "survivors": 324,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 184
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 6,
+ "displayed_kills": 1
+ }
+ },
+ "Sonya": {
+ "Bounty Temptation": {
+ "triggered": 7,
+ "displayed_kills": 1
+ }
+ }
+ },
+ "defender": {
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 4,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 8,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 5,
+ "displayed_kills": null
+ }
+ },
+ "Bradley": {
+ "Tactical Assistance": {
+ "triggered": 4,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_gatot_cadence/gwen_60m_vs_gatot_400i.json
===================================================================
--- testcases/emulator_verified/gwen_gatot_cadence/gwen_60m_vs_gatot_400i.json prev run
+++ testcases/emulator_verified/gwen_gatot_cadence/gwen_60m_vs_gatot_400i.json this run
@@ -0,0 +1,153 @@
+[
+ {
+ "test_id": "gwen_60m_vs_gatot_400i",
+ "description": "C2 battle report 2734693171294010",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "marksman_t6": 60
+ },
+ "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": 439.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_t6": 400
+ },
+ "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-24T02:23:42.000Z",
+ "randomseed": 1790216623,
+ "attacker": 0,
+ "defender": 400,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171294010",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-24T02-23-58.156Z-e0fb75af-a407-4874-a70c-dab3439fda9f.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 60,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 21,
+ "lightly_injured": 39,
+ "kills": 0
+ },
+ "defender": {
+ "troops": 400,
+ "survivors": 400,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 21
+ },
+ "troop_details": {
+ "attacker": {
+ "marksman_t6": {
+ "troops": 60,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 21,
+ "lightly_injured": 39,
+ "kills": 0
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 400,
+ "survivors": 400,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 21
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 3,
+ "displayed_kills": null
+ },
+ "Blastmaster": {
+ "triggered": 4,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": 22,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_gatot_cadence/gwen_65m_vs_gatot_200i.json
===================================================================
--- testcases/emulator_verified/gwen_gatot_cadence/gwen_65m_vs_gatot_200i.json prev run
+++ testcases/emulator_verified/gwen_gatot_cadence/gwen_65m_vs_gatot_200i.json this run
@@ -0,0 +1,153 @@
+[
+ {
+ "test_id": "gwen_65m_vs_gatot_200i",
+ "description": "C2 battle report 2734693171294108",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "marksman_t6": 65
+ },
+ "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": 439.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_t6": 200
+ },
+ "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-24T02:24:44.000Z",
+ "randomseed": 1790216685,
+ "attacker": 0,
+ "defender": 200,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171294108",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-24T02-25-00.153Z-2ca94310-9a4f-46f7-b65b-aa092194ff41.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 65,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 23,
+ "lightly_injured": 42,
+ "kills": 0
+ },
+ "defender": {
+ "troops": 200,
+ "survivors": 200,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 23
+ },
+ "troop_details": {
+ "attacker": {
+ "marksman_t6": {
+ "troops": 65,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 23,
+ "lightly_injured": 42,
+ "kills": 0
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 200,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 23
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 5,
+ "displayed_kills": null
+ },
+ "Blastmaster": {
+ "triggered": 6,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": 32,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_s1_probes/gwen_s1_a.json
===================================================================
--- testcases/emulator_verified/gwen_s1_probes/gwen_s1_a.json prev run
+++ testcases/emulator_verified/gwen_s1_probes/gwen_s1_a.json this run
@@ -0,0 +1,139 @@
+[
+ {
+ "test_id": "gwen_s1_a",
+ "description": "C2 battle report 2734693171299391",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "marksman_t6": 10
+ },
+ "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": 439.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "marksman_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-24T05:59:58.000Z",
+ "randomseed": 1790229599,
+ "attacker": 0,
+ "defender": 997,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171299391",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-24T06-00-14.187Z-f1d2d710-fc28-4500-aa4e-cbbaf7a672fa.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 10,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 4,
+ "lightly_injured": 6,
+ "kills": 2
+ },
+ "defender": {
+ "troops": 1000,
+ "survivors": 997,
+ "losses": 0,
+ "injured": 2,
+ "lightly_injured": 1,
+ "kills": 4
+ },
+ "troop_details": {
+ "attacker": {
+ "marksman_t6": {
+ "troops": 10,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 4,
+ "lightly_injured": 6,
+ "kills": 2
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 1000,
+ "survivors": 997,
+ "losses": 0,
+ "injured": 2,
+ "lightly_injured": 1,
+ "kills": 4
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": null,
+ "displayed_kills": null
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_s1_probes/gwen_s1_b.json
===================================================================
--- testcases/emulator_verified/gwen_s1_probes/gwen_s1_b.json prev run
+++ testcases/emulator_verified/gwen_s1_probes/gwen_s1_b.json this run
@@ -0,0 +1,166 @@
+[
+ {
+ "test_id": "gwen_s1_b",
+ "description": "C2 battle report 2734693171299415",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 1,
+ "marksman_t6": 10
+ },
+ "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": 439.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 1,
+ "lancer_t6": 500,
+ "marksman_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-24T06:01:08.000Z",
+ "randomseed": 1790229669,
+ "attacker": 0,
+ "defender": 1501,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171299415",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-24T06-01-24.215Z-b85f1aec-427b-47a4-aeef-f336aa5a6a78.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 11,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 5,
+ "lightly_injured": 6,
+ "kills": 0
+ },
+ "defender": {
+ "troops": 1501,
+ "survivors": 1501,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 5
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 1,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 1,
+ "lightly_injured": 0,
+ "kills": 0
+ },
+ "marksman_t6": {
+ "troops": 10,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 4,
+ "lightly_injured": 6,
+ "kills": 0
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1,
+ "survivors": 1,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 0
+ },
+ "lancer_t6": {
+ "troops": 500,
+ "survivors": 500,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 1
+ },
+ "marksman_t6": {
+ "troops": 1000,
+ "survivors": 1000,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 4
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": null,
+ "displayed_kills": null
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_s1_probes/gwen_s1_c.json
===================================================================
--- testcases/emulator_verified/gwen_s1_probes/gwen_s1_c.json prev run
+++ testcases/emulator_verified/gwen_s1_probes/gwen_s1_c.json this run
@@ -0,0 +1,166 @@
+[
+ {
+ "test_id": "gwen_s1_c",
+ "description": "C2 battle report 2734693171299444",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 5,
+ "marksman_t6": 40
+ },
+ "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": 439.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 1,
+ "lancer_t6": 500,
+ "marksman_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": 230.96,
+ "defense": 219.76,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-24T06:02:12.000Z",
+ "randomseed": 1790229733,
+ "attacker": 0,
+ "defender": 4492,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171299444",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-24T06-02-28.239Z-b8f11dfe-1c41-43f3-ac40-45789e323b21.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 45,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 16,
+ "lightly_injured": 29,
+ "kills": 4
+ },
+ "defender": {
+ "troops": 4501,
+ "survivors": 4492,
+ "losses": 0,
+ "injured": 4,
+ "lightly_injured": 5,
+ "kills": 16
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 5,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 2,
+ "lightly_injured": 3,
+ "kills": 0
+ },
+ "marksman_t6": {
+ "troops": 40,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 14,
+ "lightly_injured": 26,
+ "kills": 4
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 1,
+ "lightly_injured": 0,
+ "kills": 0
+ },
+ "lancer_t6": {
+ "troops": 500,
+ "survivors": 492,
+ "losses": 0,
+ "injured": 3,
+ "lightly_injured": 5,
+ "kills": 3
+ },
+ "marksman_t6": {
+ "troops": 4000,
+ "survivors": 4000,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 13
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": null,
+ "displayed_kills": null
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_s1_probes/gwen_s1_d.json
===================================================================
--- testcases/emulator_verified/gwen_s1_probes/gwen_s1_d.json prev run
+++ testcases/emulator_verified/gwen_s1_probes/gwen_s1_d.json this run
@@ -0,0 +1,157 @@
+[
+ {
+ "test_id": "gwen_s1_d",
+ "description": "C2 battle report 2734693171300136",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "marksman_t6": 96
+ },
+ "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": 439.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 5,
+ "lancer_t6": 500,
+ "marksman_t6": 5000
+ },
+ "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-24T06:33:09.000Z",
+ "randomseed": 1790231590,
+ "attacker": 0,
+ "defender": 5497,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171300136",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-24T06-33-25.251Z-b1bce01b-6b69-4063-90b4-1e16d97e4e79.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 96,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 34,
+ "lightly_injured": 62,
+ "kills": 4
+ },
+ "defender": {
+ "troops": 5505,
+ "survivors": 5497,
+ "losses": 0,
+ "injured": 4,
+ "lightly_injured": 4,
+ "kills": 34
+ },
+ "troop_details": {
+ "attacker": {
+ "marksman_t6": {
+ "troops": 96,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 34,
+ "lightly_injured": 62,
+ "kills": 4
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 5,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 2,
+ "lightly_injured": 3,
+ "kills": 0
+ },
+ "lancer_t6": {
+ "troops": 500,
+ "survivors": 497,
+ "losses": 0,
+ "injured": 2,
+ "lightly_injured": 1,
+ "kills": 6
+ },
+ "marksman_t6": {
+ "troops": 5000,
+ "survivors": 5000,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 28
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": null,
+ "displayed_kills": null
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_s1_probes/gwen_s1_e.json
===================================================================
--- testcases/emulator_verified/gwen_s1_probes/gwen_s1_e.json prev run
+++ testcases/emulator_verified/gwen_s1_probes/gwen_s1_e.json this run
@@ -0,0 +1,166 @@
+[
+ {
+ "test_id": "gwen_s1_e",
+ "description": "C2 battle report 2734693171300156",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 3,
+ "skill_2": 1,
+ "skill_3": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 5,
+ "marksman_t6": 64
+ },
+ "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": 439.82,
+ "defense": 440.82,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 2,
+ "lancer_t6": 500,
+ "marksman_t6": 6000
+ },
+ "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-24T06:34:42.000Z",
+ "randomseed": 1790231683,
+ "attacker": 0,
+ "defender": 6486,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171300156",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-24T06-34-58.271Z-30dd00a4-269b-48aa-9373-6dd4c64b6afe.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 69,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 25,
+ "lightly_injured": 44,
+ "kills": 6
+ },
+ "defender": {
+ "troops": 6502,
+ "survivors": 6486,
+ "losses": 0,
+ "injured": 6,
+ "lightly_injured": 10,
+ "kills": 25
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 5,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 2,
+ "lightly_injured": 3,
+ "kills": 0
+ },
+ "marksman_t6": {
+ "troops": 64,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 23,
+ "lightly_injured": 41,
+ "kills": 6
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 2,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 1,
+ "lightly_injured": 1,
+ "kills": 0
+ },
+ "lancer_t6": {
+ "troops": 500,
+ "survivors": 486,
+ "losses": 0,
+ "injured": 5,
+ "lightly_injured": 9,
+ "kills": 4
+ },
+ "marksman_t6": {
+ "troops": 6000,
+ "survivors": 6000,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 21
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": null,
+ "displayed_kills": null
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_s2_probes/gwen_s2_calibration.json
===================================================================
--- testcases/emulator_verified/gwen_s2_probes/gwen_s2_calibration.json prev run
+++ testcases/emulator_verified/gwen_s2_probes/gwen_s2_calibration.json this run
@@ -0,0 +1,143 @@
+[
+ {
+ "test_id": "gwen_s2_calibration",
+ "description": "C2 battle report 2734693171300786",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 100,
+ "marksman_t6": 400
+ },
+ "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": {
+ "infantry_t6": 400
+ },
+ "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": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-24T07:08:35.000Z",
+ "randomseed": 1790233716,
+ "attacker": 427,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171300786",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-24T07-09-00.147Z-1ed536bb-1203-4904-9c1a-55f74e3962a4.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 500,
+ "survivors": 427,
+ "losses": 0,
+ "injured": 26,
+ "lightly_injured": 47,
+ "kills": 140
+ },
+ "defender": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 26
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 100,
+ "survivors": 27,
+ "losses": 0,
+ "injured": 26,
+ "lightly_injured": 47,
+ "kills": 9
+ },
+ "marksman_t6": {
+ "troops": 400,
+ "survivors": 400,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 131
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 26
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 20,
+ "displayed_kills": 4
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_s2_probes/gwen_s2_curse_timing.json
===================================================================
--- testcases/emulator_verified/gwen_s2_probes/gwen_s2_curse_timing.json prev run
+++ testcases/emulator_verified/gwen_s2_probes/gwen_s2_curse_timing.json this run
@@ -0,0 +1,152 @@
+[
+ {
+ "test_id": "gwen_s2_curse_timing",
+ "description": "C2 battle report 2734693171300885",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 100,
+ "marksman_t6": 400
+ },
+ "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": {
+ "infantry_t6": 400,
+ "marksman_t6": 200
+ },
+ "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": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-24T07:13:39.000Z",
+ "randomseed": 1790234020,
+ "attacker": 0,
+ "defender": 423,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171300885",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-24T07-14-04.140Z-f5e7eadc-389b-438f-92c7-3cb39c9db764.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 62
+ },
+ "defender": {
+ "troops": 600,
+ "survivors": 423,
+ "losses": 0,
+ "injured": 62,
+ "lightly_injured": 115,
+ "kills": 175
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 1
+ },
+ "marksman_t6": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 61
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 400,
+ "survivors": 223,
+ "losses": 0,
+ "injured": 62,
+ "lightly_injured": 115,
+ "kills": 42
+ },
+ "marksman_t6": {
+ "troops": 200,
+ "survivors": 200,
+ "losses": 0,
+ "injured": 0,
+ "lightly_injured": 0,
+ "kills": 133
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 7,
+ "displayed_kills": 1
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_s2_probes/gwen_s2_first_trigger.json
===================================================================
--- testcases/emulator_verified/gwen_s2_probes/gwen_s2_first_trigger.json prev run
+++ testcases/emulator_verified/gwen_s2_probes/gwen_s2_first_trigger.json this run
@@ -0,0 +1,152 @@
+[
+ {
+ "test_id": "gwen_s2_first_trigger",
+ "description": "C2 battle report 2734693171300907",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 50,
+ "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": 344.4,
+ "defense": 333.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 400,
+ "marksman_t6": 200
+ },
+ "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": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-24T07:15:16.000Z",
+ "randomseed": 1790234117,
+ "attacker": 369,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171300907",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-24T07-15-41.154Z-ba8b20e7-91c1-4bb8-8ba4-728cfd859c56.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1250,
+ "survivors": 369,
+ "losses": 0,
+ "injured": 309,
+ "lightly_injured": 572,
+ "kills": 210
+ },
+ "defender": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 309
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 0
+ },
+ "marksman_t6": {
+ "troops": 1200,
+ "survivors": 369,
+ "losses": 0,
+ "injured": 291,
+ "lightly_injured": 540,
+ "kills": 210
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 51
+ },
+ "marksman_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 258
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 8,
+ "displayed_kills": 5
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_s2_probes/gwen_s2_mark_consumption.json
===================================================================
--- testcases/emulator_verified/gwen_s2_probes/gwen_s2_mark_consumption.json prev run
+++ testcases/emulator_verified/gwen_s2_probes/gwen_s2_mark_consumption.json this run
@@ -0,0 +1,152 @@
+[
+ {
+ "test_id": "gwen_s2_mark_consumption",
+ "description": "C2 battle report 2734693171300981",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 200,
+ "marksman_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": 344.4,
+ "defense": 333.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 100,
+ "marksman_t6": 200
+ },
+ "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": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-24T07:17:38.000Z",
+ "randomseed": 1790234259,
+ "attacker": 41,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171300981",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-24T07-18-03.176Z-be6f20f3-0757-4697-b312-cffcd4db25c8.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 300,
+ "survivors": 41,
+ "losses": 0,
+ "injured": 91,
+ "lightly_injured": 168,
+ "kills": 105
+ },
+ "defender": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 91
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 9
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 41,
+ "losses": 0,
+ "injured": 21,
+ "lightly_injured": 38,
+ "kills": 96
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 4
+ },
+ "marksman_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 87
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 15,
+ "displayed_kills": 3
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_1.json
===================================================================
--- testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_1.json prev run
+++ testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_1.json this run
@@ -0,0 +1,152 @@
+[
+ {
+ "test_id": "gwen_s2_source_1",
+ "description": "C2 battle report 2734693171302235",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 125,
+ "marksman_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": 344.4,
+ "defense": 333.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 200,
+ "marksman_t6": 250
+ },
+ "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": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-24T08:17:42.000Z",
+ "randomseed": 1790237863,
+ "attacker": 332,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171302235",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-24T08-18-07.123Z-4eef9a68-53c5-4a57-909c-62d350c71bc4.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 725,
+ "survivors": 332,
+ "losses": 0,
+ "injured": 138,
+ "lightly_injured": 255,
+ "kills": 158
+ },
+ "defender": {
+ "troops": 450,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 158,
+ "lightly_injured": 292,
+ "kills": 138
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 125,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 44,
+ "lightly_injured": 81,
+ "kills": 2
+ },
+ "marksman_t6": {
+ "troops": 600,
+ "survivors": 332,
+ "losses": 0,
+ "injured": 94,
+ "lightly_injured": 174,
+ "kills": 156
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 9
+ },
+ "marksman_t6": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 129
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 8,
+ "displayed_kills": 3
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_2.json
===================================================================
--- testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_2.json prev run
+++ testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_2.json this run
@@ -0,0 +1,152 @@
+[
+ {
+ "test_id": "gwen_s2_source_2",
+ "description": "C2 battle report 2734693171302281",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 125,
+ "marksman_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": 344.4,
+ "defense": 333.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 300,
+ "marksman_t6": 200
+ },
+ "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": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-24T08:19:49.000Z",
+ "randomseed": 1790237990,
+ "attacker": 737,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171302281",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-24T08-20-14.140Z-ccc3bc84-5048-4d69-97d7-95a03f728d0c.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 1125,
+ "survivors": 737,
+ "losses": 0,
+ "injured": 137,
+ "lightly_injured": 251,
+ "kills": 175
+ },
+ "defender": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 137
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 125,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 44,
+ "lightly_injured": 81,
+ "kills": 2
+ },
+ "marksman_t6": {
+ "troops": 1000,
+ "survivors": 737,
+ "losses": 0,
+ "injured": 93,
+ "lightly_injured": 170,
+ "kills": 173
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 16
+ },
+ "marksman_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 121
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 8,
+ "displayed_kills": 5
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_3.json
===================================================================
--- testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_3.json prev run
+++ testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_3.json this run
@@ -0,0 +1,152 @@
+[
+ {
+ "test_id": "gwen_s2_source_3",
+ "description": "C2 battle report 2734693171302300",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 50,
+ "marksman_t6": 400
+ },
+ "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": {
+ "infantry_t6": 150,
+ "marksman_t6": 125
+ },
+ "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": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-24T08:20:59.000Z",
+ "randomseed": 1790238060,
+ "attacker": 60,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171302300",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-24T08-21-24.126Z-fcd8ff89-287a-48fd-ab22-99aae56642fc.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 450,
+ "survivors": 60,
+ "losses": 0,
+ "injured": 137,
+ "lightly_injured": 253,
+ "kills": 97
+ },
+ "defender": {
+ "troops": 275,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 97,
+ "lightly_injured": 178,
+ "kills": 137
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 0
+ },
+ "marksman_t6": {
+ "troops": 400,
+ "survivors": 60,
+ "losses": 0,
+ "injured": 119,
+ "lightly_injured": 221,
+ "kills": 97
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 14
+ },
+ "marksman_t6": {
+ "troops": 125,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 44,
+ "lightly_injured": 81,
+ "kills": 123
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 9,
+ "displayed_kills": 2
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_4.json
===================================================================
--- testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_4.json prev run
+++ testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_4.json this run
@@ -0,0 +1,152 @@
+[
+ {
+ "test_id": "gwen_s2_source_4",
+ "description": "C2 battle report 2734693171302326",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 200,
+ "marksman_t6": 150
+ },
+ "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": {
+ "infantry_t6": 150,
+ "marksman_t6": 200
+ },
+ "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": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-24T08:22:13.000Z",
+ "randomseed": 1790238134,
+ "attacker": 0,
+ "defender": 113,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171302326",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-24T08-22-38.124Z-6985cf51-83ae-44f0-8509-12fef7ccbe26.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 350,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 123,
+ "lightly_injured": 227,
+ "kills": 84
+ },
+ "defender": {
+ "troops": 350,
+ "survivors": 113,
+ "losses": 0,
+ "injured": 84,
+ "lightly_injured": 153,
+ "kills": 123
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 5
+ },
+ "marksman_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 79
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 9
+ },
+ "marksman_t6": {
+ "troops": 200,
+ "survivors": 113,
+ "losses": 0,
+ "injured": 31,
+ "lightly_injured": 56,
+ "kills": 114
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 14,
+ "displayed_kills": 2
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_5.json
===================================================================
--- testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_5.json prev run
+++ testcases/emulator_verified/gwen_s2_source_binding/gwen_s2_source_5.json this run
@@ -0,0 +1,152 @@
+[
+ {
+ "test_id": "gwen_s2_source_5",
+ "description": "C2 battle report 2734693171302350",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Gwen": {
+ "skill_1": 2,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 50,
+ "marksman_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": 344.4,
+ "defense": 333.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "infantry_t6": 200,
+ "marksman_t6": 75
+ },
+ "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": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-24T08:23:22.000Z",
+ "randomseed": 1790238203,
+ "attacker": 383,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171302350",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-24T08-23-47.140Z-8f0fdcd6-7967-4afc-84fa-9eef13f6d292.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 650,
+ "survivors": 383,
+ "losses": 0,
+ "injured": 94,
+ "lightly_injured": 173,
+ "kills": 97
+ },
+ "defender": {
+ "troops": 275,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 97,
+ "lightly_injured": 178,
+ "kills": 94
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 50,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 18,
+ "lightly_injured": 32,
+ "kills": 1
+ },
+ "marksman_t6": {
+ "troops": 600,
+ "survivors": 383,
+ "losses": 0,
+ "injured": 76,
+ "lightly_injured": 141,
+ "kills": 96
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 70,
+ "lightly_injured": 130,
+ "kills": 17
+ },
+ "marksman_t6": {
+ "troops": 75,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 27,
+ "lightly_injured": 48,
+ "kills": 77
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 8,
+ "displayed_kills": 3
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/interaction_map/gordon_bradley_pure_lancers.json
===================================================================
--- testcases/emulator_verified/interaction_map/gordon_bradley_pure_lancers.json prev run
+++ testcases/emulator_verified/interaction_map/gordon_bradley_pure_lancers.json this run
@@ -0,0 +1,160 @@
+[
+ {
+ "test_id": "gordon_bradley_pure_lancers",
+ "description": "C2 battle report 2734693171240660",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Gordon": {
+ "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": 350
+ },
+ "stats": {
+ "infantry": {
+ "attack": 304.95,
+ "defense": 292.95,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 493.62,
+ "defense": 492.87,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 739.42,
+ "defense": 740.42,
+ "lethality": 375.19,
+ "health": 331.96
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "lancer_t6": 1800
+ },
+ "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-22T22:18:13.000Z",
+ "randomseed": 1790115494,
+ "attacker": 0,
+ "defender": 241,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171240660",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T22-18-30.101Z-8c95c337-b5dd-4d55-84c4-c9bf71a44dd7.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 350,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 123,
+ "lightly_injured": 227,
+ "kills": 546
+ },
+ "defender": {
+ "troops": 1800,
+ "survivors": 241,
+ "losses": 0,
+ "injured": 546,
+ "lightly_injured": 1013,
+ "kills": 123
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 350,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 123,
+ "lightly_injured": 227,
+ "kills": 546
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 1800,
+ "survivors": 241,
+ "losses": 0,
+ "injured": 546,
+ "lightly_injured": 1013,
+ "kills": 123
+ }
+ }
+ },
+ "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
+ }
+ },
+ "Bradley": {
+ "Tactical Assistance": {
+ "triggered": 12,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/interaction_map/gordon_wuming_pure_lancers.json
===================================================================
--- testcases/emulator_verified/interaction_map/gordon_wuming_pure_lancers.json prev run
+++ testcases/emulator_verified/interaction_map/gordon_wuming_pure_lancers.json this run
@@ -0,0 +1,154 @@
+[
+ {
+ "test_id": "gordon_wuming_pure_lancers",
+ "description": "C2 battle report 2734693171241065",
+ "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": {
+ "lancer_t6": 600
+ },
+ "stats": {
+ "infantry": {
+ "attack": 513.98,
+ "defense": 501.98,
+ "lethality": 250.71,
+ "health": 274.79
+ },
+ "lancer": {
+ "attack": 493.62,
+ "defense": 492.87,
+ "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": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "lancer_t6": 2400
+ },
+ "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-22T22:24:24.000Z",
+ "randomseed": 1790115865,
+ "attacker": 0,
+ "defender": 434,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171241065",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T22-24-41.098Z-348f904c-625c-4308-ae98-cdc1d52e9840.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 689
+ },
+ "defender": {
+ "troops": 2400,
+ "survivors": 434,
+ "losses": 0,
+ "injured": 689,
+ "lightly_injured": 1277,
+ "kills": 210
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 689
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 2400,
+ "survivors": 434,
+ "losses": 0,
+ "injured": 689,
+ "lightly_injured": 1277,
+ "kills": 210
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gordon": {
+ "Toxic Release": {
+ "triggered": 13,
+ "displayed_kills": null
+ },
+ "Venom Infusion": {
+ "triggered": 26,
+ "displayed_kills": null
+ },
+ "Chemical Terror": {
+ "triggered": 17,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/interaction_map/renee_bradley_pure_lancers.json
===================================================================
--- testcases/emulator_verified/interaction_map/renee_bradley_pure_lancers.json prev run
+++ testcases/emulator_verified/interaction_map/renee_bradley_pure_lancers.json this run
@@ -0,0 +1,152 @@
+[
+ {
+ "test_id": "renee_bradley_pure_lancers",
+ "description": "C2 battle report 2734693171240812",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Bradley": {
+ "skill_1": 5,
+ "skill_2": 4,
+ "skill_3": 3,
+ "skill_4": 1
+ }
+ },
+ "troops": {
+ "lancer_t6": 150
+ },
+ "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": 739.42,
+ "defense": 740.42,
+ "lethality": 375.19,
+ "health": 331.96
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "lancer_t6": 1400
+ },
+ "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-22T22:20:17.000Z",
+ "randomseed": 1790115618,
+ "attacker": 0,
+ "defender": 133,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171240812",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T22-20-34.089Z-2225efdf-bceb-4656-8157-b37b7dcf5a7f.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 444
+ },
+ "defender": {
+ "troops": 1400,
+ "survivors": 133,
+ "losses": 0,
+ "injured": 444,
+ "lightly_injured": 823,
+ "kills": 53
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 444
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 1400,
+ "survivors": 133,
+ "losses": 0,
+ "injured": 444,
+ "lightly_injured": 823,
+ "kills": 53
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 20,
+ "displayed_kills": null
+ }
+ },
+ "Bradley": {
+ "Tactical Assistance": {
+ "triggered": 10,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/interaction_map/renee_jasser_pure_lancers.json
===================================================================
--- testcases/emulator_verified/interaction_map/renee_jasser_pure_lancers.json prev run
+++ testcases/emulator_verified/interaction_map/renee_jasser_pure_lancers.json this run
@@ -0,0 +1,144 @@
+[
+ {
+ "test_id": "renee_jasser_pure_lancers",
+ "description": "C2 battle report 2734693171240879",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Jasser": {
+ "skill_1": 4,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "lancer_t6": 250
+ },
+ "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": 359.37,
+ "defense": 360.37,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "lancer_t6": 1800
+ },
+ "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-22T22:21:33.000Z",
+ "randomseed": 1790115694,
+ "attacker": 17,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171240879",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T22-21-50.093Z-a2ce0c11-644e-4af8-b11e-5d966f7ccab6.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 250,
+ "survivors": 17,
+ "losses": 0,
+ "injured": 82,
+ "lightly_injured": 151,
+ "kills": 630
+ },
+ "defender": {
+ "troops": 1800,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 630,
+ "lightly_injured": 1170,
+ "kills": 82
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 250,
+ "survivors": 17,
+ "losses": 0,
+ "injured": 82,
+ "lightly_injured": 151,
+ "kills": 630
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 1800,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 630,
+ "lightly_injured": 1170,
+ "kills": 82
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 23,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/interaction_map/renee_wuming_pure_lancers.json
===================================================================
--- testcases/emulator_verified/interaction_map/renee_wuming_pure_lancers.json prev run
+++ testcases/emulator_verified/interaction_map/renee_wuming_pure_lancers.json this run
@@ -0,0 +1,146 @@
+[
+ {
+ "test_id": "renee_wuming_pure_lancers",
+ "description": "C2 battle report 2734693171240945",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Wu Ming": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 2,
+ "skill_4": 0
+ },
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 250
+ },
+ "stats": {
+ "infantry": {
+ "attack": 513.98,
+ "defense": 501.98,
+ "lethality": 250.71,
+ "health": 274.79
+ },
+ "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": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "lancer_t6": 1800
+ },
+ "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-22T22:22:49.000Z",
+ "randomseed": 1790115770,
+ "attacker": 0,
+ "defender": 214,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171240945",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T22-23-06.100Z-052071e0-8658-46a9-bc71-78d8ac9a3c47.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 556
+ },
+ "defender": {
+ "troops": 1800,
+ "survivors": 214,
+ "losses": 0,
+ "injured": 556,
+ "lightly_injured": 1030,
+ "kills": 88
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 250,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 88,
+ "lightly_injured": 162,
+ "kills": 556
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 1800,
+ "survivors": 214,
+ "losses": 0,
+ "injured": 556,
+ "lightly_injured": 1030,
+ "kills": 88
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 23,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/nohero_controls/nohero_explicit_empty_100m_vs_100m.json
===================================================================
--- testcases/emulator_verified/nohero_controls/nohero_explicit_empty_100m_vs_100m.json prev run
+++ testcases/emulator_verified/nohero_controls/nohero_explicit_empty_100m_vs_100m.json this run
@@ -0,0 +1,121 @@
+[
+ {
+ "test_id": "nohero_explicit_empty_100m_vs_100m",
+ "description": "C2 battle report 2734693171297894",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {},
+ "troops": {
+ "marksman_t6": 100
+ },
+ "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": 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-24T04:53:49.000Z",
+ "randomseed": 1790225630,
+ "attacker": 62,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171297894",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-24T04-54-05.219Z-d2cd817a-f43d-4211-9846-09c474c4ad4c.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 100,
+ "survivors": 62,
+ "losses": 0,
+ "injured": 14,
+ "lightly_injured": 24,
+ "kills": 35
+ },
+ "defender": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 14
+ },
+ "troop_details": {
+ "attacker": {
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 62,
+ "losses": 0,
+ "injured": 14,
+ "lightly_injured": 24,
+ "kills": 35
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 14
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {},
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_isolation_20260925/renee_gwen_300l_100m_vs_2700l.json
===================================================================
--- testcases/emulator_verified/renee_isolation_20260925/renee_gwen_300l_100m_vs_2700l.json prev run
+++ testcases/emulator_verified/renee_isolation_20260925/renee_gwen_300l_100m_vs_2700l.json this run
@@ -0,0 +1,165 @@
+[
+ {
+ "test_id": "renee_gwen_300l_100m_vs_2700l",
+ "description": "C2 battle report 2734693171344569",
+ "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": 100
+ },
+ "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": 2700
+ },
+ "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-25T08:19:54.000Z",
+ "randomseed": 1790324395,
+ "attacker": 0,
+ "defender": 187,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171344569",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T08-20-10.049Z-4be46d45-bc54-44bf-b329-d44015892a02.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 400,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 140,
+ "lightly_injured": 260,
+ "kills": 880
+ },
+ "defender": {
+ "troops": 2700,
+ "survivors": 187,
+ "losses": 0,
+ "injured": 880,
+ "lightly_injured": 1633,
+ "kills": 140
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 300,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 105,
+ "lightly_injured": 195,
+ "kills": 456
+ },
+ "marksman_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 424
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 2700,
+ "survivors": 187,
+ "losses": 0,
+ "injured": 880,
+ "lightly_injured": 1633,
+ "kills": 140
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 12,
+ "displayed_kills": 15
+ },
+ "Blastmaster": {
+ "triggered": 9,
+ "displayed_kills": 6
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 23,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_isolation_20260925/renee_mark_source_death.json
===================================================================
--- testcases/emulator_verified/renee_isolation_20260925/renee_mark_source_death.json prev run
+++ testcases/emulator_verified/renee_isolation_20260925/renee_mark_source_death.json this run
@@ -0,0 +1,188 @@
+[
+ {
+ "test_id": "renee_mark_source_death",
+ "description": "C2 battle report 2734693171344530",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Ahmose": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t6": 80,
+ "lancer_t6": 450,
+ "marksman_t6": 1600
+ },
+ "stats": {
+ "infantry": {
+ "attack": 422.61,
+ "defense": 405.61,
+ "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": {
+ "infantry_t6": 1200,
+ "lancer_t6": 1200,
+ "marksman_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": 297.4,
+ "defense": 286.2,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-25T08:17:36.000Z",
+ "randomseed": 1790324257,
+ "attacker": 184,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171344530",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-25T08-17-52.027Z-1936b5fc-f7c3-4861-b1bb-bbf609c3c508.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 2130,
+ "survivors": 184,
+ "losses": 0,
+ "injured": 682,
+ "lightly_injured": 1264,
+ "kills": 1050
+ },
+ "defender": {
+ "troops": 3000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 1050,
+ "lightly_injured": 1950,
+ "kills": 682
+ },
+ "troop_details": {
+ "attacker": {
+ "infantry_t6": {
+ "troops": 80,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 28,
+ "lightly_injured": 52,
+ "kills": 4
+ },
+ "lancer_t6": {
+ "troops": 450,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 158,
+ "lightly_injured": 292,
+ "kills": 167
+ },
+ "marksman_t6": {
+ "troops": 1600,
+ "survivors": 184,
+ "losses": 0,
+ "injured": 496,
+ "lightly_injured": 920,
+ "kills": 879
+ }
+ },
+ "defender": {
+ "infantry_t6": {
+ "troops": 1200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 420,
+ "lightly_injured": 780,
+ "kills": 19
+ },
+ "lancer_t6": {
+ "troops": 1200,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 420,
+ "lightly_injured": 780,
+ "kills": 206
+ },
+ "marksman_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 457
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Ahmose": {
+ "Viper Formation": {
+ "triggered": 1,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 18,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_s2_bucket_confirmations/gatot_shield_500_150.json
===================================================================
--- testcases/emulator_verified/renee_s2_bucket_confirmations/gatot_shield_500_150.json prev run
+++ testcases/emulator_verified/renee_s2_bucket_confirmations/gatot_shield_500_150.json this run
@@ -0,0 +1,149 @@
+[
+ {
+ "test_id": "gatot_shield_500_150",
+ "description": "C2 battle report 2734693171255861",
+ "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": 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": {
+ "Gatot": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t9": 150
+ },
+ "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-23T03:02:22.000Z",
+ "randomseed": 1790132543,
+ "attacker": 157,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171255861",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-23T03-02-43.099Z-ca20fbc3-5de1-4d29-a07a-a15a3bec7041.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 500,
+ "survivors": 157,
+ "losses": 0,
+ "injured": 121,
+ "lightly_injured": 222,
+ "kills": 53
+ },
+ "defender": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 121
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 500,
+ "survivors": 157,
+ "losses": 0,
+ "injured": 121,
+ "lightly_injured": 222,
+ "kills": 53
+ }
+ },
+ "defender": {
+ "infantry_t9": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 121
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 93,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": 186,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_s2_bucket_confirmations/gwen_next_hit_150_1050.json
===================================================================
--- testcases/emulator_verified/renee_s2_bucket_confirmations/gwen_next_hit_150_1050.json prev run
+++ testcases/emulator_verified/renee_s2_bucket_confirmations/gwen_next_hit_150_1050.json this run
@@ -0,0 +1,156 @@
+[
+ {
+ "test_id": "gwen_next_hit_150_1050",
+ "description": "C2 battle report 2734693171255782",
+ "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": 150
+ },
+ "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": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "lancer_t6": 1050
+ },
+ "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-23T02:57:45.000Z",
+ "randomseed": 1790132266,
+ "attacker": 0,
+ "defender": 111,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171255782",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-23T02-58-06.094Z-04f345fe-3bcb-4fee-ac76-0047566b8a9e.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 329
+ },
+ "defender": {
+ "troops": 1050,
+ "survivors": 111,
+ "losses": 0,
+ "injured": 329,
+ "lightly_injured": 610,
+ "kills": 53
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 329
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 1050,
+ "survivors": 111,
+ "losses": 0,
+ "injured": 329,
+ "lightly_injured": 610,
+ "kills": 53
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 7,
+ "displayed_kills": 3
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 23,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_s2_families/defense_gatot.json
===================================================================
--- testcases/emulator_verified/renee_s2_families/defense_gatot.json prev run
+++ testcases/emulator_verified/renee_s2_families/defense_gatot.json this run
@@ -0,0 +1,149 @@
+[
+ {
+ "test_id": "defense_gatot",
+ "description": "C2 battle report 2734693171252715",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 750
+ },
+ "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": {
+ "Gatot": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t9": 225
+ },
+ "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-23T01:23:08.000Z",
+ "randomseed": 1790126589,
+ "attacker": 339,
+ "defender": 0,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171252715",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-23T01-23-29.105Z-de756fa0-f794-4b26-9336-675b3ff5ae23.json",
+ "winner": "attacker",
+ "attacker": {
+ "troops": 750,
+ "survivors": 339,
+ "losses": 0,
+ "injured": 144,
+ "lightly_injured": 267,
+ "kills": 79
+ },
+ "defender": {
+ "troops": 225,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 79,
+ "lightly_injured": 146,
+ "kills": 144
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 750,
+ "survivors": 339,
+ "losses": 0,
+ "injured": 144,
+ "lightly_injured": 267,
+ "kills": 79
+ }
+ },
+ "defender": {
+ "infantry_t9": {
+ "troops": 225,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 79,
+ "lightly_injured": 146,
+ "kills": 144
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 73,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Gatot": {
+ "King's Bestowal": {
+ "triggered": 147,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_s2_families/defense_hendrik.json
===================================================================
--- testcases/emulator_verified/renee_s2_families/defense_hendrik.json prev run
+++ testcases/emulator_verified/renee_s2_families/defense_hendrik.json this run
@@ -0,0 +1,153 @@
+[
+ {
+ "test_id": "defense_hendrik",
+ "description": "C2 battle report 2734693171245400",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 1500
+ },
+ "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": {
+ "Hendrik": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "marksman_t6": 3750
+ },
+ "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-22T23:27:03.000Z",
+ "randomseed": 1790119624,
+ "attacker": 0,
+ "defender": 1266,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171245400",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T23-27-20.096Z-157ab07e-c18d-41f7-952b-e2d82c09dd57.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 1500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 525,
+ "lightly_injured": 975,
+ "kills": 870
+ },
+ "defender": {
+ "troops": 3750,
+ "survivors": 1266,
+ "losses": 0,
+ "injured": 870,
+ "lightly_injured": 1614,
+ "kills": 525
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 1500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 525,
+ "lightly_injured": 975,
+ "kills": 870
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 3750,
+ "survivors": 1266,
+ "losses": 0,
+ "injured": 870,
+ "lightly_injured": 1614,
+ "kills": 525
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 10,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {
+ "Hendrik": {
+ "Dagon's Heir": {
+ "triggered": 6,
+ "displayed_kills": 36
+ },
+ "Armor of Barnacles": {
+ "triggered": 5,
+ "displayed_kills": null
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_s2_families/defense_logan.json
===================================================================
--- testcases/emulator_verified/renee_s2_families/defense_logan.json prev run
+++ testcases/emulator_verified/renee_s2_families/defense_logan.json this run
@@ -0,0 +1,140 @@
+[
+ {
+ "test_id": "defense_logan",
+ "description": "C2 battle report 2734693171252843",
+ "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": 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": {
+ "Logan": {
+ "skill_1": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "marksman_t6": 3500
+ },
+ "stats": {
+ "infantry": {
+ "attack": 307.37,
+ "defense": 299.92,
+ "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-23T01:28:23.000Z",
+ "randomseed": 1790126904,
+ "attacker": 0,
+ "defender": 356,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171252843",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-23T01-28-44.093Z-27976ce7-e06d-48d8-adcb-9daf5d10124b.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 1101
+ },
+ "defender": {
+ "troops": 3500,
+ "survivors": 356,
+ "losses": 0,
+ "injured": 1101,
+ "lightly_injured": 2043,
+ "kills": 175
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 1101
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 3500,
+ "survivors": 356,
+ "losses": 0,
+ "injured": 1101,
+ "lightly_injured": 2043,
+ "kills": 175
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 18,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_s2_families/defense_lumak.json
===================================================================
--- testcases/emulator_verified/renee_s2_families/defense_lumak.json prev run
+++ testcases/emulator_verified/renee_s2_families/defense_lumak.json this run
@@ -0,0 +1,140 @@
+[
+ {
+ "test_id": "defense_lumak",
+ "description": "C2 battle report 2734693171245257",
+ "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": 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": {
+ "Lumak Bokan": {
+ "skill_1": 3,
+ "skill_2": 1
+ }
+ },
+ "troops": {
+ "marksman_t6": 3500
+ },
+ "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-22T23:24:24.000Z",
+ "randomseed": 1790119465,
+ "attacker": 0,
+ "defender": 742,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171245257",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T23-24-41.096Z-e0e3fcfb-ff3a-469f-832e-3335e0476621.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 966
+ },
+ "defender": {
+ "troops": 3500,
+ "survivors": 742,
+ "losses": 0,
+ "injured": 966,
+ "lightly_injured": 1792,
+ "kills": 175
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 966
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 3500,
+ "survivors": 742,
+ "losses": 0,
+ "injured": 966,
+ "lightly_injured": 1792,
+ "kills": 175
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 16,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_s2_families/defense_patrick.json
===================================================================
--- testcases/emulator_verified/renee_s2_families/defense_patrick.json prev run
+++ testcases/emulator_verified/renee_s2_families/defense_patrick.json this run
@@ -0,0 +1,140 @@
+[
+ {
+ "test_id": "defense_patrick",
+ "description": "C2 battle report 2734693171245547",
+ "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": 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": {
+ "Patrick": {
+ "skill_1": 4,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "marksman_t6": 3000
+ },
+ "stats": {
+ "infantry": {
+ "attack": 257.13,
+ "defense": 249.68,
+ "lethality": 188,
+ "health": 193.5
+ },
+ "lancer": {
+ "attack": 293.2,
+ "defense": 292.5,
+ "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-22T23:29:37.000Z",
+ "randomseed": 1790119778,
+ "attacker": 0,
+ "defender": 721,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171245547",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T23-29-54.100Z-044fcf49-7a90-4ced-bd36-b93a49e5c633.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 798
+ },
+ "defender": {
+ "troops": 3000,
+ "survivors": 721,
+ "losses": 0,
+ "injured": 798,
+ "lightly_injured": 1481,
+ "kills": 175
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 798
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 3000,
+ "survivors": 721,
+ "losses": 0,
+ "injured": 798,
+ "lightly_injured": 1481,
+ "kills": 175
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 15,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_s2_families/defense_sergey.json
===================================================================
--- testcases/emulator_verified/renee_s2_families/defense_sergey.json prev run
+++ testcases/emulator_verified/renee_s2_families/defense_sergey.json this run
@@ -0,0 +1,140 @@
+[
+ {
+ "test_id": "defense_sergey",
+ "description": "C2 battle report 2734693171245004",
+ "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": 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": {
+ "Sergey": {
+ "skill_1": 2,
+ "skill_2": 2
+ }
+ },
+ "troops": {
+ "marksman_t6": 3500
+ },
+ "stats": {
+ "infantry": {
+ "attack": 346.14,
+ "defense": 338.69,
+ "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-22T23:20:44.000Z",
+ "randomseed": 1790119245,
+ "attacker": 0,
+ "defender": 917,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171245004",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T23-21-01.102Z-b0fdf93d-2e08-4aa7-bc08-c65ef06b44e4.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 905
+ },
+ "defender": {
+ "troops": 3500,
+ "survivors": 917,
+ "losses": 0,
+ "injured": 905,
+ "lightly_injured": 1678,
+ "kills": 175
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 500,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 175,
+ "lightly_injured": 325,
+ "kills": 905
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 3500,
+ "survivors": 917,
+ "losses": 0,
+ "injured": 905,
+ "lightly_injured": 1678,
+ "kills": 175
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 16,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_s2_families/defense_wuming.json
===================================================================
--- testcases/emulator_verified/renee_s2_families/defense_wuming.json prev run
+++ testcases/emulator_verified/renee_s2_families/defense_wuming.json this run
@@ -0,0 +1,142 @@
+[
+ {
+ "test_id": "defense_wuming",
+ "description": "C2 battle report 2734693171252668",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 2000
+ },
+ "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": {
+ "Wu Ming": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "infantry_t9": 2000
+ },
+ "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-23T01:21:30.000Z",
+ "randomseed": 1790126491,
+ "attacker": 0,
+ "defender": 329,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171252668",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-23T01-21-51.099Z-ec52c5d3-9f33-4a5b-8628-06b291c1eee5.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 2000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 700,
+ "lightly_injured": 1300,
+ "kills": 585
+ },
+ "defender": {
+ "troops": 2000,
+ "survivors": 329,
+ "losses": 0,
+ "injured": 585,
+ "lightly_injured": 1086,
+ "kills": 700
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 2000,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 700,
+ "lightly_injured": 1300,
+ "kills": 585
+ }
+ },
+ "defender": {
+ "infantry_t9": {
+ "troops": 2000,
+ "survivors": 329,
+ "losses": 0,
+ "injured": 585,
+ "lightly_injured": 1086,
+ "kills": 700
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 48,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_s2_families/renee_edith_hendrik.json
===================================================================
--- testcases/emulator_verified/renee_s2_families/renee_edith_hendrik.json prev run
+++ testcases/emulator_verified/renee_s2_families/renee_edith_hendrik.json this run
@@ -0,0 +1,157 @@
+[
+ {
+ "test_id": "renee_edith_hendrik",
+ "description": "C2 battle report 2734693171244433",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Edith": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Hendrik": {
+ "skill_1": 1,
+ "skill_2": 1,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 150
+ },
+ "stats": {
+ "infantry": {
+ "attack": 526.6,
+ "defense": 514.6,
+ "lethality": 244.04,
+ "health": 238.39
+ },
+ "lancer": {
+ "attack": 612.83,
+ "defense": 612.08,
+ "lethality": 224.32,
+ "health": 229.95
+ },
+ "marksman": {
+ "attack": 449.21,
+ "defense": 450.21,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "lancer_t6": 1300
+ },
+ "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-22T23:13:29.000Z",
+ "randomseed": 1790118810,
+ "attacker": 0,
+ "defender": 269,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171244433",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T23-13-46.098Z-66bc4c6f-259a-454e-a210-b7c27e477c25.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 361
+ },
+ "defender": {
+ "troops": 1300,
+ "survivors": 269,
+ "losses": 0,
+ "injured": 361,
+ "lightly_injured": 670,
+ "kills": 53
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 361
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 1300,
+ "survivors": 269,
+ "losses": 0,
+ "injured": 361,
+ "lightly_injured": 670,
+ "kills": 53
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 23,
+ "displayed_kills": null
+ }
+ },
+ "Hendrik": {
+ "Armor of Barnacles": {
+ "triggered": 11,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_s2_families/renee_gwen.json
===================================================================
--- testcases/emulator_verified/renee_s2_families/renee_gwen.json prev run
+++ testcases/emulator_verified/renee_s2_families/renee_gwen.json this run
@@ -0,0 +1,165 @@
+[
+ {
+ "test_id": "renee_gwen",
+ "description": "C2 battle report 2734693171244855",
+ "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": 125,
+ "marksman_t6": 1
+ },
+ "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": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "lancer_t6": 975
+ },
+ "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-22T23:18:45.000Z",
+ "randomseed": 1790119126,
+ "attacker": 0,
+ "defender": 243,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171244855",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T23-19-02.101Z-b6c174ca-43b7-49e3-8f13-5d4b9b2eb2cc.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 126,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 45,
+ "lightly_injured": 81,
+ "kills": 257
+ },
+ "defender": {
+ "troops": 975,
+ "survivors": 243,
+ "losses": 0,
+ "injured": 257,
+ "lightly_injured": 475,
+ "kills": 45
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 125,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 44,
+ "lightly_injured": 81,
+ "kills": 230
+ },
+ "marksman_t6": {
+ "troops": 1,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 1,
+ "lightly_injured": 0,
+ "kills": 27
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 975,
+ "survivors": 243,
+ "losses": 0,
+ "injured": 257,
+ "lightly_injured": 475,
+ "kills": 45
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 12,
+ "displayed_kills": 3
+ },
+ "Blastmaster": {
+ "triggered": 8,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 20,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_s2_families/renee_gwen_25_marksmen.json
===================================================================
--- testcases/emulator_verified/renee_s2_families/renee_gwen_25_marksmen.json prev run
+++ testcases/emulator_verified/renee_s2_families/renee_gwen_25_marksmen.json this run
@@ -0,0 +1,165 @@
+[
+ {
+ "test_id": "renee_gwen_25_marksmen",
+ "description": "C2 battle report 2734693171253084",
+ "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": 125,
+ "marksman_t6": 25
+ },
+ "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": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "lancer_t6": 1100
+ },
+ "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-23T01:34:29.000Z",
+ "randomseed": 1790127270,
+ "attacker": 0,
+ "defender": 162,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171253084",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-23T01-34-50.101Z-106dbcf3-feb3-49d9-a3fa-1e947fba87ed.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 150,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 53,
+ "lightly_injured": 97,
+ "kills": 329
+ },
+ "defender": {
+ "troops": 1100,
+ "survivors": 162,
+ "losses": 0,
+ "injured": 329,
+ "lightly_injured": 609,
+ "kills": 53
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 125,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 44,
+ "lightly_injured": 81,
+ "kills": 198
+ },
+ "marksman_t6": {
+ "troops": 25,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 9,
+ "lightly_injured": 16,
+ "kills": 131
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 1100,
+ "survivors": 162,
+ "losses": 0,
+ "injured": 329,
+ "lightly_injured": 609,
+ "kills": 53
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 12,
+ "displayed_kills": 5
+ },
+ "Blastmaster": {
+ "triggered": 8,
+ "displayed_kills": 1
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 21,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_s2_families/renee_gwen_no_marksmen.json
===================================================================
--- testcases/emulator_verified/renee_s2_families/renee_gwen_no_marksmen.json prev run
+++ testcases/emulator_verified/renee_s2_families/renee_gwen_no_marksmen.json this run
@@ -0,0 +1,156 @@
+[
+ {
+ "test_id": "renee_gwen_no_marksmen",
+ "description": "C2 battle report 2734693171253006",
+ "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": 125
+ },
+ "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": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "lancer_t6": 975
+ },
+ "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-23T01:32:06.000Z",
+ "randomseed": 1790127127,
+ "attacker": 0,
+ "defender": 305,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171253006",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-23T01-32-27.097Z-66a7d1ad-30b1-4e2d-bbde-e950d2afdfc9.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 125,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 44,
+ "lightly_injured": 81,
+ "kills": 235
+ },
+ "defender": {
+ "troops": 975,
+ "survivors": 305,
+ "losses": 0,
+ "injured": 235,
+ "lightly_injured": 435,
+ "kills": 44
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 125,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 44,
+ "lightly_injured": 81,
+ "kills": 235
+ }
+ },
+ "defender": {
+ "lancer_t6": {
+ "troops": 975,
+ "survivors": 305,
+ "losses": 0,
+ "injured": 235,
+ "lightly_injured": 435,
+ "kills": 44
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Gwen": {
+ "Air Dominance": {
+ "triggered": 6,
+ "displayed_kills": 2
+ },
+ "Blastmaster": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 19,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_s2_families/renee_seo_charge.json
===================================================================
--- testcases/emulator_verified/renee_s2_families/renee_seo_charge.json prev run
+++ testcases/emulator_verified/renee_s2_families/renee_seo_charge.json this run
@@ -0,0 +1,144 @@
+[
+ {
+ "test_id": "renee_seo_charge",
+ "description": "C2 battle report 2734693171244593",
+ "attacker": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Renee": {
+ "skill_1": 4,
+ "skill_2": 4,
+ "skill_3": 4,
+ "skill_4": 0
+ },
+ "Seo-yoon": {
+ "skill_1": 4,
+ "skill_2": 3
+ }
+ },
+ "troops": {
+ "lancer_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": 359.37,
+ "defense": 360.37,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 2,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "marksman_t6": 640
+ },
+ "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-22T23:15:52.000Z",
+ "randomseed": 1790118953,
+ "attacker": 0,
+ "defender": 167,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171244593",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T23-16-09.095Z-0a431c58-909b-4bd9-b30e-40c56a87811b.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 166
+ },
+ "defender": {
+ "troops": 640,
+ "survivors": 167,
+ "losses": 0,
+ "injured": 166,
+ "lightly_injured": 307,
+ "kills": 35
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 100,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 35,
+ "lightly_injured": 65,
+ "kills": 166
+ }
+ },
+ "defender": {
+ "marksman_t6": {
+ "troops": 640,
+ "survivors": 167,
+ "losses": 0,
+ "injured": 166,
+ "lightly_injured": 307,
+ "kills": 35
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 13,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Index: testcases/emulator_verified/renee_s2_families/skill_fleet_captain.json
===================================================================
--- testcases/emulator_verified/renee_s2_families/skill_fleet_captain.json prev run
+++ testcases/emulator_verified/renee_s2_families/skill_fleet_captain.json this run
@@ -0,0 +1,853 @@
+[
+ {
+ "test_id": "skill_fleet_captain",
+ "description": "C2 battle report 2734693171244469",
+ "attacker": {
+ "name": "[BBQ]XxWIPxX",
+ "heroes": {
+ "Renee": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ },
+ "Wayne": {
+ "skill_1": 3,
+ "skill_2": 3,
+ "skill_3": 3,
+ "skill_4": 0
+ }
+ },
+ "troops": {
+ "lancer_t6": 600
+ },
+ "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": 402.68,
+ "defense": 391.48,
+ "lethality": 181.53,
+ "health": 177.16
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "defender": {
+ "name": "[ARK]Piddlyminxxx",
+ "heroes": {
+ "Jasser": {
+ "skill_1": 4,
+ "skill_2": 4
+ }
+ },
+ "troops": {
+ "infantry_t9": 800
+ },
+ "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": 359.37,
+ "defense": 360.37,
+ "lethality": 235.76,
+ "health": 230.26
+ }
+ },
+ "joiner_heroes": {}
+ },
+ "engagement_type": "solo",
+ "game_report_result": [
+ {
+ "timestamp": "2026-09-22T23:13:42.000Z",
+ "randomseed": 1790118823,
+ "attacker": 0,
+ "defender": 142,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171244469",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T23-14-10.095Z-735cc25a-570d-4946-afee-aed1f9cfff51.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 231
+ },
+ "defender": {
+ "troops": 800,
+ "survivors": 142,
+ "losses": 0,
+ "injured": 231,
+ "lightly_injured": 427,
+ "kills": 210
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 231
+ }
+ },
+ "defender": {
+ "infantry_t9": {
+ "troops": 800,
+ "survivors": 142,
+ "losses": 0,
+ "injured": 231,
+ "lightly_injured": 427,
+ "kills": 210
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Wayne": {
+ "Fleet": {
+ "triggered": 8,
+ "displayed_kills": 16
+ },
+ "Thunder Strike": {
+ "triggered": 20,
+ "displayed_kills": 12
+ },
+ "Roundabout Hit": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 41,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-22T23:14:52.000Z",
+ "randomseed": 1790118893,
+ "attacker": 0,
+ "defender": 114,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171244510",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T23-15-20.108Z-a9e08cd6-0825-457d-aa34-a79bb954abc8.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 241
+ },
+ "defender": {
+ "troops": 800,
+ "survivors": 114,
+ "losses": 0,
+ "injured": 241,
+ "lightly_injured": 445,
+ "kills": 210
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 241
+ }
+ },
+ "defender": {
+ "infantry_t9": {
+ "troops": 800,
+ "survivors": 114,
+ "losses": 0,
+ "injured": 241,
+ "lightly_injured": 445,
+ "kills": 210
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Wayne": {
+ "Fleet": {
+ "triggered": 11,
+ "displayed_kills": 20
+ },
+ "Thunder Strike": {
+ "triggered": 21,
+ "displayed_kills": 13
+ },
+ "Roundabout Hit": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 42,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-22T23:16:02.000Z",
+ "randomseed": 1790118963,
+ "attacker": 0,
+ "defender": 115,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171244602",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T23-16-30.111Z-c3f01fb5-9f53-40d2-bc40-353717d01840.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 240
+ },
+ "defender": {
+ "troops": 800,
+ "survivors": 115,
+ "losses": 0,
+ "injured": 240,
+ "lightly_injured": 445,
+ "kills": 210
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 240
+ }
+ },
+ "defender": {
+ "infantry_t9": {
+ "troops": 800,
+ "survivors": 115,
+ "losses": 0,
+ "injured": 240,
+ "lightly_injured": 445,
+ "kills": 210
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Wayne": {
+ "Fleet": {
+ "triggered": 14,
+ "displayed_kills": 23
+ },
+ "Thunder Strike": {
+ "triggered": 21,
+ "displayed_kills": 12
+ },
+ "Roundabout Hit": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 42,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-22T23:17:12.000Z",
+ "randomseed": 1790119033,
+ "attacker": 0,
+ "defender": 107,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171244680",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T23-17-40.097Z-8cad79aa-198d-4567-8908-2e358dc0c2ad.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 243
+ },
+ "defender": {
+ "troops": 800,
+ "survivors": 107,
+ "losses": 0,
+ "injured": 243,
+ "lightly_injured": 450,
+ "kills": 210
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 243
+ }
+ },
+ "defender": {
+ "infantry_t9": {
+ "troops": 800,
+ "survivors": 107,
+ "losses": 0,
+ "injured": 243,
+ "lightly_injured": 450,
+ "kills": 210
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Wayne": {
+ "Fleet": {
+ "triggered": 14,
+ "displayed_kills": 24
+ },
+ "Thunder Strike": {
+ "triggered": 21,
+ "displayed_kills": 12
+ },
+ "Roundabout Hit": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 42,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-22T23:18:22.000Z",
+ "randomseed": 1790119103,
+ "attacker": 0,
+ "defender": 117,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171244831",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T23-18-50.103Z-4741e745-275e-4636-a19d-a5ec881663c8.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 240
+ },
+ "defender": {
+ "troops": 800,
+ "survivors": 117,
+ "losses": 0,
+ "injured": 240,
+ "lightly_injured": 443,
+ "kills": 210
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 240
+ }
+ },
+ "defender": {
+ "infantry_t9": {
+ "troops": 800,
+ "survivors": 117,
+ "losses": 0,
+ "injured": 240,
+ "lightly_injured": 443,
+ "kills": 210
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Wayne": {
+ "Fleet": {
+ "triggered": 11,
+ "displayed_kills": 20
+ },
+ "Thunder Strike": {
+ "triggered": 21,
+ "displayed_kills": 14
+ },
+ "Roundabout Hit": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 42,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-22T23:26:02.000Z",
+ "randomseed": 1790119563,
+ "attacker": 0,
+ "defender": 136,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171245332",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T23-26-30.108Z-0a07c369-a883-4d2e-b797-07bd6e7e55e7.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 233
+ },
+ "defender": {
+ "troops": 800,
+ "survivors": 136,
+ "losses": 0,
+ "injured": 233,
+ "lightly_injured": 431,
+ "kills": 210
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 233
+ }
+ },
+ "defender": {
+ "infantry_t9": {
+ "troops": 800,
+ "survivors": 136,
+ "losses": 0,
+ "injured": 233,
+ "lightly_injured": 431,
+ "kills": 210
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Wayne": {
+ "Fleet": {
+ "triggered": 11,
+ "displayed_kills": 15
+ },
+ "Thunder Strike": {
+ "triggered": 20,
+ "displayed_kills": 13
+ },
+ "Roundabout Hit": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 41,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-22T23:27:12.000Z",
+ "randomseed": 1790119633,
+ "attacker": 0,
+ "defender": 96,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171245405",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T23-27-40.095Z-e17e782b-6667-4581-ad5d-64db8bdf57d3.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 247
+ },
+ "defender": {
+ "troops": 800,
+ "survivors": 96,
+ "losses": 0,
+ "injured": 247,
+ "lightly_injured": 457,
+ "kills": 210
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 247
+ }
+ },
+ "defender": {
+ "infantry_t9": {
+ "troops": 800,
+ "survivors": 96,
+ "losses": 0,
+ "injured": 247,
+ "lightly_injured": 457,
+ "kills": 210
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Wayne": {
+ "Fleet": {
+ "triggered": 17,
+ "displayed_kills": 24
+ },
+ "Thunder Strike": {
+ "triggered": 21,
+ "displayed_kills": 13
+ },
+ "Roundabout Hit": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 43,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-22T23:28:22.000Z",
+ "randomseed": 1790119703,
+ "attacker": 0,
+ "defender": 189,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171245491",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T23-28-50.100Z-5ef9f66f-9537-407c-8e8c-434645b7c829.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 214
+ },
+ "defender": {
+ "troops": 800,
+ "survivors": 189,
+ "losses": 0,
+ "injured": 214,
+ "lightly_injured": 397,
+ "kills": 210
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 214
+ }
+ },
+ "defender": {
+ "infantry_t9": {
+ "troops": 800,
+ "survivors": 189,
+ "losses": 0,
+ "injured": 214,
+ "lightly_injured": 397,
+ "kills": 210
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Wayne": {
+ "Fleet": {
+ "triggered": 3,
+ "displayed_kills": 5
+ },
+ "Thunder Strike": {
+ "triggered": 19,
+ "displayed_kills": 12
+ },
+ "Roundabout Hit": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 39,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-22T23:29:32.000Z",
+ "randomseed": 1790119773,
+ "attacker": 0,
+ "defender": 121,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171245535",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T23-30-00.101Z-c6148a9e-2687-4d83-8422-66a62d5f1f48.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 238
+ },
+ "defender": {
+ "troops": 800,
+ "survivors": 121,
+ "losses": 0,
+ "injured": 238,
+ "lightly_injured": 441,
+ "kills": 210
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 238
+ }
+ },
+ "defender": {
+ "infantry_t9": {
+ "troops": 800,
+ "survivors": 121,
+ "losses": 0,
+ "injured": 238,
+ "lightly_injured": 441,
+ "kills": 210
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Wayne": {
+ "Fleet": {
+ "triggered": 12,
+ "displayed_kills": 21
+ },
+ "Thunder Strike": {
+ "triggered": 21,
+ "displayed_kills": 12
+ },
+ "Roundabout Hit": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 42,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ },
+ {
+ "timestamp": "2026-09-22T23:30:42.000Z",
+ "randomseed": 1790119843,
+ "attacker": 0,
+ "defender": 120,
+ "metadata": {
+ "source": "c2",
+ "report_reference": "2734693171245600",
+ "battle_type": "atk_camp",
+ "stat_units": "percentage_points",
+ "raw_trace_file": "../../../c2/data/runs/2026-09-22T23-31-10.094Z-1e9ce1bd-e7d1-4997-b97b-f7ba31b0850c.json",
+ "winner": "defender",
+ "attacker": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 238
+ },
+ "defender": {
+ "troops": 800,
+ "survivors": 120,
+ "losses": 0,
+ "injured": 238,
+ "lightly_injured": 442,
+ "kills": 210
+ },
+ "troop_details": {
+ "attacker": {
+ "lancer_t6": {
+ "troops": 600,
+ "survivors": 0,
+ "losses": 0,
+ "injured": 210,
+ "lightly_injured": 390,
+ "kills": 238
+ }
+ },
+ "defender": {
+ "infantry_t9": {
+ "troops": 800,
+ "survivors": 120,
+ "losses": 0,
+ "injured": 238,
+ "lightly_injured": 442,
+ "kills": 210
+ }
+ }
+ },
+ "skill_details": {
+ "attacker": {
+ "Wayne": {
+ "Fleet": {
+ "triggered": 11,
+ "displayed_kills": 19
+ },
+ "Thunder Strike": {
+ "triggered": 21,
+ "displayed_kills": 12
+ },
+ "Roundabout Hit": {
+ "triggered": null,
+ "displayed_kills": null
+ }
+ },
+ "Renee": {
+ "Nightmare Trace": {
+ "triggered": 42,
+ "displayed_kills": null
+ }
+ }
+ },
+ "defender": {}
+ }
+ }
+ }
+ ]
+ }
+]
Show raw per-run patches
Run B 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/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/simulator.test.ts b/simulator/src/simulator.test.ts
--- a/simulator/src/simulator.test.ts
+++ b/simulator/src/simulator.test.ts
@@ -21,6 +21,50 @@
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("delayed damage settles shields on arrival in recorded Gatot battles", () => {
const config = loadSimulatorConfig();
for (const [path, survivors] of [