1450 lines
76 KiB
C#
1450 lines
76 KiB
C#
using System.Collections.Generic;
|
|
|
|
namespace IdleSpectator;
|
|
|
|
/// <summary>
|
|
/// Built-in agent playtest scenarios expanded by <c>action=scenario</c>.
|
|
/// </summary>
|
|
internal static class HarnessScenarios
|
|
{
|
|
public static List<HarnessCommand> Build(string name)
|
|
{
|
|
switch ((name ?? "").Trim().ToLowerInvariant())
|
|
{
|
|
case "smoke":
|
|
return Smoke();
|
|
case "tip_match":
|
|
return TipMatch();
|
|
case "critical":
|
|
case "critical_smoke":
|
|
return CriticalSmoke();
|
|
case "settings_full":
|
|
return SettingsFull();
|
|
case "ghost_guard":
|
|
return GhostGuard();
|
|
case "retarget_stability":
|
|
return RetargetStability();
|
|
case "input_exit":
|
|
return InputExit();
|
|
case "director_tiers":
|
|
return DirectorTiers();
|
|
case "director_lifecycle":
|
|
return DirectorLifecycle();
|
|
case "interest_happiness":
|
|
return InterestHappiness();
|
|
case "director_gaps":
|
|
case "director_coverage":
|
|
return DirectorGaps();
|
|
case "watch_reason":
|
|
case "watch_reason_clarity":
|
|
return WatchReasonClarity();
|
|
case "director_action_priority":
|
|
case "action_priority":
|
|
return DirectorActionPriority();
|
|
case "discovery":
|
|
return Discovery();
|
|
case "world_log":
|
|
return WorldLog();
|
|
case "chronicle":
|
|
case "chronicle_smoke":
|
|
return ChronicleSmoke();
|
|
case "activity":
|
|
case "activity_idle_smoke":
|
|
return ActivityIdleSmoke();
|
|
case "activity_status_smoke":
|
|
return ActivityStatusSmoke();
|
|
case "activity_happiness_smoke":
|
|
return ActivityHappinessSmoke();
|
|
case "activity_happiness_audit":
|
|
return ActivityHappinessAudit();
|
|
case "activity_prose_showcase":
|
|
return ActivityProseShowcase();
|
|
case "activity_taxonomy_audit":
|
|
return ActivityTaxonomyAudit();
|
|
case "activity_status_audit":
|
|
return ActivityStatusAudit();
|
|
case "activity_rate":
|
|
case "activity_rate_sample":
|
|
return ActivityRateSample();
|
|
case "chronicle_subject_bench":
|
|
case "subject_bench":
|
|
return ChronicleSubjectBench();
|
|
case "regression":
|
|
case "full":
|
|
return Regression();
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private static List<HarnessCommand> ActivityTaxonomyAudit()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("ata0", "wait_world"),
|
|
Step("ata1", "pick_unit", asset: "auto"),
|
|
Step("ata2", "focus", asset: "auto"),
|
|
Step("ata3", "assert", expect: "activity_taxonomy_audit"),
|
|
Step("ata4", "snapshot")
|
|
};
|
|
}
|
|
|
|
/// <summary>Dump sample activity lines for a few species (dev / product check).</summary>
|
|
private static List<HarnessCommand> ActivityProseShowcase()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("aps0", "wait_world"),
|
|
Step("aps1", "dismiss_windows"),
|
|
Step("aps2", "assert", expect: "activity_prose_showcase"),
|
|
Step("aps3", "snapshot")
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Timed Fallen/Living list rebuilds at rising subject counts.
|
|
/// Picks the highest size under a 2ms Fallen avg budget (runtime cap only for that session).
|
|
/// </summary>
|
|
private static List<HarnessCommand> ChronicleSubjectBench()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("sb0", "wait_world"),
|
|
Step("sb1", "dismiss_windows"),
|
|
Step("sb2", "chronicle_clear"),
|
|
Step(
|
|
"sb3",
|
|
"chronicle_subject_bench",
|
|
value: "320,640,1280,2560,4096,8192",
|
|
count: 3,
|
|
expect: "12"),
|
|
Step("sb4", "assert", expect: "chronicle_subjects_capped"),
|
|
Step("sb5", "snapshot")
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Full regression meta-suite (nested scenarios keep assert counters).
|
|
/// Battle / map-gen paths stay out of gate.
|
|
/// </summary>
|
|
private static List<HarnessCommand> Regression()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Nested("reg_critical", "critical_smoke"),
|
|
Nested("reg_settings", "settings_full"),
|
|
Nested("reg_ghost", "ghost_guard"),
|
|
Nested("reg_retarget", "retarget_stability"),
|
|
Nested("reg_input", "input_exit"),
|
|
Nested("reg_director", "director_tiers"),
|
|
Nested("reg_director_life", "director_lifecycle"),
|
|
Nested("reg_interest_happiness", "interest_happiness"),
|
|
Nested("reg_director_gaps", "director_gaps"),
|
|
Nested("reg_action_priority", "director_action_priority"),
|
|
Nested("reg_discovery", "discovery"),
|
|
Nested("reg_worldlog", "world_log"),
|
|
Nested("reg_chronicle", "chronicle_smoke"),
|
|
Nested("reg_activity", "activity_idle_smoke"),
|
|
Nested("reg_activity_status", "activity_status_smoke"),
|
|
Nested("reg_activity_happiness", "activity_happiness_smoke"),
|
|
Step("reg_end", "fast_timing", value: "false"),
|
|
Step("reg_snap", "snapshot"),
|
|
};
|
|
}
|
|
|
|
private static List<HarnessCommand> ActivityHappinessAudit()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("aha0", "wait_world"),
|
|
Step("aha1", "pick_unit", asset: "auto"),
|
|
Step("aha2", "focus", asset: "auto"),
|
|
Step("aha3", "assert", expect: "activity_happiness_audit"),
|
|
Step("aha4", "snapshot")
|
|
};
|
|
}
|
|
|
|
private static List<HarnessCommand> ActivityHappinessSmoke()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("hp0", "wait_world"),
|
|
Step("hp1", "dismiss_windows"),
|
|
Step("hp1b", "spectator", value: "off"),
|
|
Step("hp2", "spawn", asset: "human"),
|
|
Step("hp3", "focus", asset: "auto"),
|
|
Step("hp4", "activity_clear"),
|
|
Step("hp5", "happiness_reset"),
|
|
Step("hp6", "assert", expect: "activity_happiness_audit"),
|
|
|
|
// Signal + Life: crown
|
|
Step("hp10", "happiness_apply", value: "become_king"),
|
|
Step("hp11", "assert", expect: "activity_happiness_task_id", value: "happiness:become_king"),
|
|
Step("hp12", "assert", expect: "activity_happiness_ring_count", value: "1", label: "exact"),
|
|
Step("hp13", "assert", expect: "activity_happiness_life_count", value: "1", label: "min"),
|
|
Step("hp14", "assert", expect: "activity_log_contains", value: "throne"),
|
|
|
|
// Ambient cooldown: second meal within window should not add another ring write
|
|
Step("hp20", "happiness_reset"),
|
|
Step("hp21", "happiness_apply", value: "just_ate"),
|
|
Step("hp22", "assert", expect: "activity_happiness_ring_count", value: "1", label: "exact"),
|
|
Step("hp23", "happiness_apply", value: "just_ate"),
|
|
Step("hp24", "assert", expect: "activity_happiness_count", value: "2", label: "exact"),
|
|
Step("hp25", "assert", expect: "activity_happiness_ring_count", value: "1", label: "exact"),
|
|
|
|
// Force related grief prose
|
|
Step("hp30", "happiness_reset"),
|
|
Step("hp31", "happiness_force_note", value: "death_child", label: "Ava"),
|
|
Step("hp32", "assert", expect: "activity_log_contains", value: "Ava"),
|
|
Step("hp33", "assert", expect: "activity_happiness_task_id", value: "happiness:death_child"),
|
|
|
|
// Real lover grief path
|
|
Step("hp40", "spawn", asset: "human"),
|
|
Step("hp41", "happiness_remember_partner"),
|
|
Step("hp42", "spawn", asset: "human"),
|
|
Step("hp43", "focus", asset: "auto"),
|
|
Step("hp44", "activity_clear"),
|
|
Step("hp45", "happiness_reset"),
|
|
Step("hp46", "happiness_bond_lovers"),
|
|
Step("hp47", "wait", value: "0.5"),
|
|
Step("hp48", "happiness_kill_partner"),
|
|
Step("hp49", "wait", value: "0.5"),
|
|
Step("hp50", "assert", expect: "activity_log_contains", value: "lover"),
|
|
Step("hp51", "assert", expect: "activity_happiness_life_count", value: "1", label: "min"),
|
|
|
|
// Friend bond + canonical merge path
|
|
Step("hp60", "spawn", asset: "human"),
|
|
Step("hp61", "happiness_remember_partner"),
|
|
Step("hp62", "spawn", asset: "human"),
|
|
Step("hp63", "focus", asset: "auto"),
|
|
Step("hp64", "activity_clear"),
|
|
Step("hp65", "happiness_reset"),
|
|
Step("hp66", "happiness_bond_friends"),
|
|
Step("hp67", "assert", expect: "activity_happiness_count", value: "1", label: "min"),
|
|
|
|
// Full catalog inject through real API
|
|
Step("hp70", "happiness_reset"),
|
|
Step("hp71", "happiness_inject_all"),
|
|
Step("hp72", "assert", expect: "activity_happiness_count", value: "1", label: "min"),
|
|
|
|
// Aggregate burst budget
|
|
Step("hp80", "happiness_reset"),
|
|
Step("hp81", "happiness_burst", value: "just_started_war", count: 48),
|
|
|
|
Step("hp90", "snapshot")
|
|
};
|
|
}
|
|
|
|
private static List<HarnessCommand> ActivityStatusAudit()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("asa0", "wait_world"),
|
|
Step("asa1", "pick_unit", asset: "auto"),
|
|
Step("asa2", "focus", asset: "auto"),
|
|
Step("asa3", "assert", expect: "activity_status_audit"),
|
|
Step("asa4", "snapshot")
|
|
};
|
|
}
|
|
|
|
private static List<HarnessCommand> ActivityStatusSmoke()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("st0", "dismiss_windows"),
|
|
Step("st1", "wait_world"),
|
|
Step("st2", "set_setting", expect: "enabled", value: "true"),
|
|
Step("st3", "set_setting", expect: "show_dossier_caption", value: "true"),
|
|
Step("st4", "activity_clear"),
|
|
Step("st5", "activity_status_reset"),
|
|
Step("st5b", "spawn", asset: "human", count: 1),
|
|
Step("st6", "pick_unit", asset: "human"),
|
|
Step("st7", "spectator", value: "off"),
|
|
Step("st8", "spectator", value: "on"),
|
|
Step("st9", "focus", asset: "auto"),
|
|
Step("st10", "wait", wait: 0.25f),
|
|
Step("st10b", "activity_status_reset"),
|
|
Step("st11", "assert", expect: "activity_status_audit"),
|
|
|
|
// Gain path (confused is widely applicable across assets)
|
|
Step("st12", "status_apply", value: "confused"),
|
|
Step("st13", "assert", expect: "activity_status_gain_count", value: "1", label: "exact"),
|
|
Step("st14", "assert", expect: "activity_log_contains", value: "confused"),
|
|
Step("st15", "assert", expect: "activity_status_task_id", value: "status_gain:confused"),
|
|
Step("st16", "assert", expect: "activity_status_active", value: "confused", label: "true"),
|
|
|
|
// Refresh must not create another gain line
|
|
Step("st17", "status_apply", value: "confused"),
|
|
Step("st18", "assert", expect: "activity_status_gain_count", value: "1", label: "exact"),
|
|
Step("st19", "assert", expect: "activity_status_refresh_count", value: "1", label: "min"),
|
|
|
|
// Second distinct gain
|
|
Step("st20", "status_apply", value: "sleeping"),
|
|
Step("st21", "assert", expect: "activity_status_gain_count", value: "2", label: "exact"),
|
|
Step("st22", "assert", expect: "activity_log_contains", value: "asleep"),
|
|
|
|
// Explicit removal
|
|
Step("st23", "status_remove", value: "confused"),
|
|
Step("st24", "wait", wait: 0.15f),
|
|
Step("st25", "assert", expect: "activity_status_loss_count", value: "1", label: "min"),
|
|
Step("st26", "assert", expect: "activity_log_contains", value: "clarity"),
|
|
Step("st27", "assert", expect: "activity_status_active", value: "confused", label: "false"),
|
|
|
|
// Authored burning phrase via force path (game may block burning on some units)
|
|
Step("st27b", "status_force_note", value: "burning", expect: "gain"),
|
|
Step("st27c", "assert", expect: "activity_log_contains", value: "catches fire"),
|
|
|
|
// Fresh living unit for expiry / clear / death sections
|
|
Step("st28", "spawn", asset: "human", count: 1),
|
|
Step("st28a", "pick_unit", asset: "human"),
|
|
Step("st28b", "focus", asset: "auto"),
|
|
Step("st28c", "activity_status_reset"),
|
|
Step("st28d", "fast_timing", value: "true"),
|
|
Step("st29", "status_apply", value: "surprised", label: "0.25"),
|
|
Step("st30", "assert", expect: "activity_status_gain_count", value: "1", label: "exact"),
|
|
Step("st31", "wait", wait: 1.25f),
|
|
Step("st32", "assert", expect: "activity_status_active", value: "surprised", label: "false"),
|
|
Step("st33", "assert", expect: "activity_status_loss_count", value: "1", label: "min"),
|
|
Step("st33b", "fast_timing", value: "false"),
|
|
|
|
// Bulk clear on another fresh unit
|
|
Step("st34", "spawn", asset: "human", count: 1),
|
|
Step("st34b", "pick_unit", asset: "human"),
|
|
Step("st34c", "focus", asset: "auto"),
|
|
Step("st34d", "activity_status_reset"),
|
|
Step("st35", "status_apply", value: "confused"),
|
|
Step("st36", "status_apply", value: "caffeinated"),
|
|
Step("st37", "status_clear_all"),
|
|
Step("st38", "wait", wait: 0.15f),
|
|
Step("st39", "assert", expect: "activity_status_loss_count", value: "2", label: "min"),
|
|
|
|
// Unknown fallback force note
|
|
Step("st40", "status_force_note", value: "zzz_harness_fake_status", expect: "gain"),
|
|
Step("st41", "assert", expect: "activity_log_contains", value: "zzz harness fake status"),
|
|
|
|
// Dead actor blocks gain; death dispose does not spam losses
|
|
Step("st42", "spawn", asset: "human", count: 1),
|
|
Step("st42b", "pick_unit", asset: "human"),
|
|
Step("st42c", "focus", asset: "auto"),
|
|
Step("st42d", "activity_status_reset"),
|
|
Step("st43", "status_apply", value: "poisoned"),
|
|
Step("st44", "assert", expect: "activity_status_gain_count", value: "1", label: "exact"),
|
|
Step("st45", "kill_focus"),
|
|
Step("st46", "wait", wait: 0.2f),
|
|
Step("st47", "assert", expect: "activity_status_loss_count", value: "0", label: "exact"),
|
|
Step("st48", "status_apply", value: "confused", expect: "blocked"),
|
|
Step("st49", "assert", expect: "activity_status_gain_count", value: "1", label: "exact"),
|
|
|
|
// UI on a fresh living unit
|
|
Step("st50", "spawn", asset: "human", count: 1),
|
|
Step("st50b", "pick_unit", asset: "human"),
|
|
Step("st51", "focus", asset: "auto"),
|
|
Step("st52", "activity_clear"),
|
|
Step("st53", "activity_status_reset"),
|
|
Step("st54", "status_apply", value: "confused"),
|
|
Step("st55", "status_apply", value: "sleeping"),
|
|
Step("st56", "assert", expect: "dossier_history_shown", value: "1", label: "min", tier: "activity"),
|
|
Step("st57", "assert", expect: "caption_layout_ok"),
|
|
Step("st58", "screenshot", value: "hud-activity-status-smoke.png"),
|
|
Step("st59", "wait", wait: 0.45f),
|
|
Step("st60", "lore_open_focus"),
|
|
Step("st61", "wait", wait: 0.2f),
|
|
Step("st62", "lore_detail_pane", value: "activity"),
|
|
Step("st63", "assert", expect: "lore_activity_shown", value: "1", label: "min"),
|
|
Step("st64", "assert", expect: "no_bad"),
|
|
Step("st99", "snapshot"),
|
|
};
|
|
}
|
|
|
|
private static List<HarnessCommand> Smoke()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("s0", "dismiss_windows"),
|
|
Step("s1", "wait_world"),
|
|
Step("s2", "reset_counters"),
|
|
Step("s3", "pick_unit", asset: "auto"),
|
|
Step("s4", "spectator", value: "off"),
|
|
Step("s5", "assert", expect: "idle", value: "false"),
|
|
Step("s6", "spectator", value: "on"),
|
|
Step("s7", "focus", asset: "auto"),
|
|
Step("s8", "wait", wait: 0.35f),
|
|
Step("s9", "assert", expect: "health"),
|
|
Step("s10", "snapshot"),
|
|
};
|
|
}
|
|
|
|
private static List<HarnessCommand> TipMatch()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("t0", "dismiss_windows"),
|
|
Step("t1", "wait_world"),
|
|
Step("t2", "reset_counters"),
|
|
Step("t3", "pick_unit", asset: "auto"),
|
|
Step("t4", "spectator", value: "on"),
|
|
Step("t5", "watch", asset: "auto", label: "New species: {asset}", tier: "Curiosity"),
|
|
Step("t6", "wait", wait: 0.3f),
|
|
Step("t7", "assert", expect: "tip_matches_unit"),
|
|
Step("t8", "assert", expect: "unit_asset", asset: "auto"),
|
|
Step("t9", "assert", expect: "power_bar", value: "false"),
|
|
Step("t10", "snapshot"),
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Critical E2E against already-living map units (no fragile miracle spawns).
|
|
/// </summary>
|
|
private static List<HarnessCommand> CriticalSmoke()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("c00", "dismiss_windows"),
|
|
Step("c01", "wait_world"),
|
|
Step("c02", "reset_counters"),
|
|
|
|
Step("c05", "pick_unit", asset: "auto"),
|
|
Step("c06", "wait", wait: 0.25f),
|
|
|
|
Step("c10", "set_setting", expect: "enabled", value: "false"),
|
|
Step("c11", "assert", expect: "enabled", value: "false"),
|
|
Step("c12", "spectator", value: "on", expect: "blocked"),
|
|
Step("c13", "assert", expect: "idle", value: "false"),
|
|
Step("c14", "set_setting", expect: "enabled", value: "true"),
|
|
Step("c15", "assert", expect: "enabled", value: "true"),
|
|
|
|
Step("c20", "spectator", value: "off"),
|
|
Step("c21", "assert", expect: "idle", value: "false"),
|
|
Step("c22", "spectator", value: "on"),
|
|
Step("c23", "focus", asset: "auto"),
|
|
Step("c24", "reset_counters"),
|
|
Step("c25", "wait", wait: 0.35f),
|
|
Step("c26", "assert", expect: "health"),
|
|
Step("c27", "assert", expect: "power_bar", value: "false"),
|
|
Step("c28", "assert", expect: "no_bad"),
|
|
Step("c29", "assert", expect: "focus_arrows"),
|
|
|
|
Step("c30", "watch", asset: "auto", label: "New species: {asset}", tier: "Curiosity"),
|
|
Step("c31", "wait", wait: 0.3f),
|
|
Step("c32", "assert", expect: "tip_matches_unit"),
|
|
Step("c33", "assert", expect: "unit_asset", asset: "auto"),
|
|
Step("c34", "assert", expect: "tip_contains", value: "auto"),
|
|
Step("c35", "assert", expect: "power_bar", value: "false"),
|
|
Step("c36", "assert", expect: "no_bad"),
|
|
|
|
Step("c40", "watch", asset: "auto", label: "Retarget A: {asset}", tier: "Curiosity"),
|
|
Step("c41", "wait", wait: 0.25f),
|
|
Step("c42", "focus", asset: "auto"),
|
|
Step("c43", "wait", wait: 0.25f),
|
|
Step("c44", "watch", asset: "auto", label: "Retarget B: {asset}", tier: "Curiosity"),
|
|
Step("c45", "wait", wait: 0.35f),
|
|
Step("c46", "assert", expect: "health"),
|
|
Step("c47", "assert", expect: "no_bad"),
|
|
Step("c48", "assert", expect: "power_bar", value: "false"),
|
|
|
|
Step("c50", "simulate_input"),
|
|
Step("c51", "wait", wait: 0.25f),
|
|
Step("c52", "assert", expect: "idle", value: "false"),
|
|
Step("c52b", "assert", expect: "caption_contains", value: "Paused"),
|
|
Step("c53", "assert", expect: "no_bad"),
|
|
|
|
Step("c99", "snapshot"),
|
|
};
|
|
}
|
|
|
|
private static List<HarnessCommand> SettingsFull()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("sf0", "dismiss_windows"),
|
|
Step("sf1", "wait_world"),
|
|
Step("sf2", "set_setting", expect: "enabled", value: "true"),
|
|
Step("sf3", "pick_unit", asset: "auto"),
|
|
|
|
Step("sf10", "set_setting", expect: "enabled", value: "false"),
|
|
Step("sf11", "assert", expect: "enabled", value: "false"),
|
|
Step("sf12", "spectator", value: "on", expect: "blocked"),
|
|
Step("sf13", "assert", expect: "idle", value: "false"),
|
|
|
|
Step("sf20", "set_setting", expect: "enabled", value: "true"),
|
|
Step("sf21", "spectator", value: "on"),
|
|
Step("sf22", "focus", asset: "auto"),
|
|
Step("sf23", "assert", expect: "idle", value: "true"),
|
|
|
|
// Disable while active must turn idle off (settings callback).
|
|
Step("sf30", "set_setting", expect: "enabled", value: "false"),
|
|
Step("sf31", "assert", expect: "idle", value: "false"),
|
|
Step("sf32", "assert", expect: "enabled", value: "false"),
|
|
|
|
Step("sf40", "set_setting", expect: "enabled", value: "true"),
|
|
Step("sf41", "set_setting", expect: "show_watch_reasons", value: "false"),
|
|
Step("sf42", "assert", expect: "show_watch_reasons", value: "false"),
|
|
Step("sf43", "set_setting", expect: "show_watch_reasons", value: "true"),
|
|
Step("sf44", "assert", expect: "show_watch_reasons", value: "true"),
|
|
|
|
Step("sf99", "snapshot"),
|
|
};
|
|
}
|
|
|
|
private static List<HarnessCommand> GhostGuard()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("g0", "dismiss_windows"),
|
|
Step("g1", "wait_world"),
|
|
Step("g2", "set_setting", expect: "enabled", value: "true"),
|
|
Step("g3", "pick_unit", asset: "auto"),
|
|
Step("g4", "spectator", value: "on"),
|
|
Step("g5", "focus", asset: "auto"),
|
|
Step("g6", "reset_counters"),
|
|
Step("g7", "remember_focus"),
|
|
Step("g8", "watch_ghost"),
|
|
Step("g9", "wait", wait: 0.25f),
|
|
Step("g10", "assert", expect: "focus_same"),
|
|
Step("g11", "assert", expect: "health"),
|
|
Step("g12", "assert", expect: "no_bad"),
|
|
Step("g13", "assert", expect: "power_bar", value: "false"),
|
|
Step("g99", "snapshot"),
|
|
};
|
|
}
|
|
|
|
private static List<HarnessCommand> RetargetStability()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("rs0", "dismiss_windows"),
|
|
Step("rs1", "wait_world"),
|
|
Step("rs2", "set_setting", expect: "enabled", value: "true"),
|
|
Step("rs3", "pick_unit", asset: "auto"),
|
|
Step("rs4", "spectator", value: "on"),
|
|
Step("rs5", "focus", asset: "auto"),
|
|
Step("rs6", "reset_counters"),
|
|
|
|
Step("rs10", "watch", asset: "auto", label: "Stab 1: {asset}", tier: "Curiosity"),
|
|
Step("rs11", "wait", wait: 0.2f),
|
|
Step("rs12", "watch", asset: "auto", label: "Stab 2: {asset}", tier: "Curiosity"),
|
|
Step("rs13", "wait", wait: 0.2f),
|
|
Step("rs14", "watch", asset: "auto", label: "Stab 3: {asset}", tier: "Curiosity"),
|
|
Step("rs15", "wait", wait: 0.2f),
|
|
Step("rs16", "watch", asset: "auto", label: "Stab 4: {asset}", tier: "Curiosity"),
|
|
Step("rs17", "wait", wait: 0.25f),
|
|
|
|
Step("rs20", "assert", expect: "health"),
|
|
Step("rs21", "assert", expect: "no_bad"),
|
|
Step("rs22", "assert", expect: "power_bar", value: "false"),
|
|
Step("rs23", "assert", expect: "unit_asset", asset: "auto"),
|
|
Step("rs99", "snapshot"),
|
|
};
|
|
}
|
|
|
|
private static List<HarnessCommand> InputExit()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("ie0", "dismiss_windows"),
|
|
Step("ie1", "wait_world"),
|
|
Step("ie2", "set_setting", expect: "enabled", value: "true"),
|
|
Step("ie3", "fast_timing", value: "true"),
|
|
Step("ie4", "pick_unit", asset: "auto"),
|
|
Step("ie5", "spectator", value: "off"),
|
|
Step("ie6", "spectator", value: "on"),
|
|
Step("ie7", "focus", asset: "auto"),
|
|
|
|
// Still inside post-enable grace: drag must not exit.
|
|
Step("ie10", "assert", expect: "in_grace", value: "true"),
|
|
Step("ie11", "simulate_input", value: "drag", expect: "grace"),
|
|
Step("ie12", "assert", expect: "idle", value: "true"),
|
|
|
|
Step("ie20", "expire_grace"),
|
|
Step("ie21", "assert", expect: "in_grace", value: "false"),
|
|
Step("ie22", "simulate_input", value: "drag"),
|
|
Step("ie23", "assert", expect: "idle", value: "false"),
|
|
Step("ie24", "assert", expect: "no_bad"),
|
|
|
|
Step("ie90", "fast_timing", value: "false"),
|
|
Step("ie99", "snapshot"),
|
|
};
|
|
}
|
|
|
|
private static List<HarnessCommand> DirectorTiers()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("dt0", "dismiss_windows"),
|
|
Step("dt1", "wait_world"),
|
|
Step("dt2", "set_setting", expect: "enabled", value: "true"),
|
|
Step("dt3", "fast_timing", value: "true"),
|
|
// Calm unit + spectator restart clears collector so world Story tips cannot steal CurioA.
|
|
Step("dt4", "spawn", asset: "sheep"),
|
|
Step("dt5", "spectator", value: "off"),
|
|
Step("dt6", "spectator", value: "on"),
|
|
Step("dt7", "focus", asset: "sheep"),
|
|
Step("dt8", "reset_counters"),
|
|
|
|
// Curiosity can replace ambient after rotate window.
|
|
Step("dt10", "trigger_interest", asset: "sheep", label: "CurioA", tier: "Curiosity"),
|
|
Step("dt11", "age_current", wait: 2f),
|
|
Step("dt12", "director_run", wait: 1.2f),
|
|
Step("dt13", "assert", expect: "current_tier", value: "Curiosity"),
|
|
Step("dt14", "assert", expect: "tip_contains", value: "CurioA"),
|
|
|
|
// Action interrupts curiosity after high-tier window.
|
|
Step("dt20", "trigger_interest", asset: "auto", label: "ActionA", tier: "Action"),
|
|
Step("dt21", "age_current", wait: 1.2f),
|
|
Step("dt22", "director_run", wait: 1.2f),
|
|
Step("dt23", "assert", expect: "current_tier", value: "Action"),
|
|
Step("dt24", "assert", expect: "tip_contains", value: "ActionA"),
|
|
|
|
// Curiosity must not be accepted while Action holds the camera (no director_run:
|
|
// ambient rotate would otherwise steal Action after dwell).
|
|
Step("dt30", "trigger_interest", asset: "auto", label: "CurioBlocked", tier: "Curiosity"),
|
|
Step("dt31", "assert", expect: "current_tier", value: "Action"),
|
|
Step("dt32", "assert", expect: "tip_contains", value: "ActionA"),
|
|
Step("dt33", "assert", expect: "would_accept_curiosity", value: "false"),
|
|
Step("dt34", "assert", expect: "health"),
|
|
Step("dt35", "assert", expect: "no_bad"),
|
|
|
|
Step("dt90", "fast_timing", value: "false"),
|
|
Step("dt99", "snapshot"),
|
|
};
|
|
}
|
|
|
|
private static List<HarnessCommand> DirectorLifecycle()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("dl0", "dismiss_windows"),
|
|
Step("dl1", "wait_world"),
|
|
Step("dl2", "set_setting", expect: "enabled", value: "true"),
|
|
Step("dl3", "fast_timing", value: "true"),
|
|
Step("dl4", "spawn", asset: "sheep"),
|
|
Step("dl5", "spectator", value: "off"),
|
|
Step("dl6", "spectator", value: "on"),
|
|
Step("dl7", "focus", asset: "sheep"),
|
|
|
|
// Protected Action: Story waits.
|
|
Step("dl10", "interest_force_session", asset: "sheep", label: "HoldAction", tier: "Action", expect: "hold_action"),
|
|
Step("dl11", "assert", expect: "current_tier", value: "Action"),
|
|
Step("dl12", "assert", expect: "session_active", value: "true"),
|
|
Step("dl13", "trigger_interest", asset: "sheep", label: "StoryWait", tier: "Story"),
|
|
Step("dl14", "age_current", wait: 1.2f),
|
|
Step("dl15", "director_run", wait: 1.2f),
|
|
Step("dl16", "assert", expect: "current_tier", value: "Action"),
|
|
Step("dl17", "assert", expect: "tip_contains", value: "HoldAction"),
|
|
|
|
// Epic preempts Action after settle.
|
|
Step("dl20", "trigger_interest", asset: "sheep", label: "EpicWin", tier: "Epic"),
|
|
Step("dl21", "age_current", wait: 1.2f),
|
|
Step("dl22", "director_run", wait: 1.2f),
|
|
Step("dl23", "assert", expect: "current_tier", value: "Epic"),
|
|
Step("dl24", "assert", expect: "tip_contains", value: "EpicWin"),
|
|
Step("dl25", "assert", expect: "no_bad"),
|
|
|
|
Step("dl90", "fast_timing", value: "false"),
|
|
Step("dl99", "snapshot"),
|
|
};
|
|
}
|
|
|
|
private static List<HarnessCommand> InterestHappiness()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("ih0", "dismiss_windows"),
|
|
Step("ih1", "wait_world"),
|
|
Step("ih2", "set_setting", expect: "enabled", value: "true"),
|
|
Step("ih3", "fast_timing", value: "true"),
|
|
Step("ih4", "spawn", asset: "human"),
|
|
Step("ih5", "spectator", value: "off"),
|
|
Step("ih6", "spectator", value: "on"),
|
|
Step("ih7", "focus", asset: "auto"),
|
|
Step("ih8", "happiness_reset"),
|
|
|
|
// Force Action hold; ambient happiness must not steal.
|
|
Step("ih10", "interest_force_session", asset: "auto", label: "ActionHold", tier: "Action", expect: "action_hold"),
|
|
Step("ih11", "happiness_apply", value: "just_ate"),
|
|
Step("ih12", "age_current", wait: 1.2f),
|
|
Step("ih13", "director_run", wait: 1.2f),
|
|
Step("ih14", "assert", expect: "current_tier", value: "Action"),
|
|
Step("ih15", "assert", expect: "tip_contains", value: "ActionHold"),
|
|
|
|
// Grief Signal registers into registry (force-note + direct register).
|
|
Step("ih20", "happiness_force_note", value: "death_child", label: "Ava"),
|
|
Step("ih21", "assert", expect: "interest_has_key", value: "death_child"),
|
|
Step("ih22", "assert", expect: "current_tier", value: "Action"),
|
|
Step("ih23", "assert", expect: "happiness_drain_seq"),
|
|
|
|
// Epic can leave Action; grief may then surface.
|
|
Step("ih30", "trigger_interest", asset: "auto", label: "EpicClear", tier: "Epic"),
|
|
Step("ih31", "age_current", wait: 1.2f),
|
|
Step("ih32", "director_run", wait: 1.2f),
|
|
Step("ih33", "assert", expect: "current_tier", value: "Epic"),
|
|
Step("ih34", "assert", expect: "no_bad"),
|
|
|
|
Step("ih90", "fast_timing", value: "false"),
|
|
Step("ih99", "snapshot"),
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Full director gap matrix: resume, variety, lifecycle edges, scoring, happiness filters.
|
|
/// </summary>
|
|
private static List<HarnessCommand> DirectorGaps()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("dg0", "dismiss_windows"),
|
|
Step("dg1", "wait_world"),
|
|
Step("dg2", "set_setting", expect: "enabled", value: "true"),
|
|
Step("dg3", "fast_timing", value: "true"),
|
|
Step("dg4", "spawn", asset: "sheep", count: 2),
|
|
Step("dg5", "spectator", value: "off"),
|
|
Step("dg6", "spectator", value: "on"),
|
|
Step("dg7", "focus", asset: "sheep"),
|
|
Step("dg8", "interest_variety_clear"),
|
|
|
|
// --- Epic preempts Story; Story does not preempt itself past Action rules ---
|
|
Step("dg10", "interest_force_session", asset: "sheep", label: "HoldStory", tier: "Story", expect: "hold_story"),
|
|
Step("dg11", "assert", expect: "current_tier", value: "Story"),
|
|
Step("dg12", "trigger_interest", asset: "sheep", label: "ActionBlocked", tier: "Action"),
|
|
Step("dg13", "age_current", wait: 1.2f),
|
|
Step("dg14", "director_run", wait: 1.2f),
|
|
Step("dg15", "assert", expect: "current_tier", value: "Story"),
|
|
Step("dg16", "assert", expect: "tip_contains", value: "HoldStory"),
|
|
Step("dg17", "trigger_interest", asset: "sheep", label: "EpicOverStory", tier: "Epic"),
|
|
Step("dg18", "age_current", wait: 1.2f),
|
|
Step("dg19", "director_run", wait: 1.2f),
|
|
Step("dg20", "assert", expect: "current_tier", value: "Epic"),
|
|
Step("dg21", "assert", expect: "tip_contains", value: "EpicOverStory"),
|
|
|
|
// --- Resume after Epic: Action hold interrupted then restored ---
|
|
Step("dg30", "spectator", value: "off"),
|
|
Step("dg31", "spectator", value: "on"),
|
|
Step("dg32", "focus", asset: "sheep"),
|
|
Step("dg33", "interest_force_session", asset: "sheep", label: "HoldResume", tier: "Action", expect: "hold_resume"),
|
|
Step("dg34", "assert", expect: "session_key", value: "hold_resume"),
|
|
Step("dg35", "trigger_interest", asset: "sheep", label: "EpicInterrupt", tier: "Epic"),
|
|
Step("dg36", "age_current", wait: 1.2f),
|
|
Step("dg37", "director_run", wait: 1.2f),
|
|
Step("dg38", "assert", expect: "current_tier", value: "Epic"),
|
|
Step("dg39", "assert", expect: "interrupted_key", value: "hold_resume"),
|
|
// Max-cap the Epic scene (fast timing caps ~8s) so EndCurrent resumes Action.
|
|
Step("dg40", "age_current", wait: 9f),
|
|
Step("dg41", "director_run", wait: 1.2f),
|
|
Step("dg42", "assert", expect: "tip_contains", value: "HoldResume"),
|
|
Step("dg43", "assert", expect: "current_tier", value: "Action"),
|
|
Step("dg44", "assert", expect: "interrupted_key", value: "none"),
|
|
|
|
// --- Max cap ends a forced scene ---
|
|
Step("dg50", "interest_force_session", asset: "sheep", label: "CapMe", tier: "Action", expect: "cap_me"),
|
|
Step("dg51", "age_current", wait: 9f),
|
|
Step("dg52", "director_run", wait: 1.2f),
|
|
Step("dg54", "assert", expect: "interest_no_key", value: "cap_me"),
|
|
|
|
// --- Quiet grace: inactive scene ends after grace ---
|
|
Step("dg60", "interest_force_session", asset: "sheep", label: "QuietEnd", tier: "Action", expect: "quiet_end"),
|
|
Step("dg61", "interest_mark_inactive", value: "1.0"),
|
|
Step("dg62", "assert", expect: "session_active", value: "false"),
|
|
Step("dg63", "director_run", wait: 1.2f),
|
|
Step("dg64", "assert", expect: "interest_no_key", value: "quiet_end"),
|
|
|
|
// --- Stale expiry / dedupe refresh ---
|
|
Step("dg70", "interest_inject", asset: "sheep", label: "StaleTip", tier: "Curiosity", expect: "stale_tip", value: "lead=character;ttl=0.01"),
|
|
Step("dg71", "assert", expect: "interest_has_key", value: "stale_tip"),
|
|
Step("dg72", "interest_expire_pending", value: "stale_tip"),
|
|
Step("dg73", "assert", expect: "interest_no_key", value: "stale_tip"),
|
|
Step("dg74", "interest_inject", asset: "sheep", label: "RefreshA", tier: "Action", expect: "refresh_a", value: "lead=event;evt=20"),
|
|
Step("dg75", "interest_inject", asset: "sheep", label: "RefreshA", tier: "Action", expect: "refresh_a", value: "lead=event;evt=90"),
|
|
Step("dg76", "assert", expect: "interest_registry_count", value: "1", label: "refresh_a"),
|
|
|
|
// --- Death handoff: FollowUnit cleared → RelatedUnit ---
|
|
Step("dg80", "interest_force_session", asset: "sheep", label: "HandoffHold", tier: "Action", expect: "handoff_hold"),
|
|
Step("dg81", "interest_clear_follow", asset: "sheep"),
|
|
Step("dg82", "director_run", wait: 0.8f),
|
|
Step("dg83", "assert", expect: "session_key", value: "handoff_hold"),
|
|
Step("dg84", "assert", expect: "session_active", value: "true"),
|
|
|
|
// --- Soft 70/30: character-heavy mix prefers event when both pools exist ---
|
|
Step("dg90", "interest_end_session"),
|
|
Step("dg91", "interest_variety_clear"),
|
|
Step("dg91b", "interest_expire_pending", value: ""),
|
|
Step("dg92", "interest_variety_seed", value: "2:10"),
|
|
Step("dg93", "assert", expect: "interest_event_share", value: "0.17", label: "0.2"),
|
|
Step("dg94", "interest_inject", asset: "sheep", label: "EvtWin", tier: "Curiosity", expect: "evt_win", value: "lead=event;evt=80"),
|
|
Step("dg95", "interest_inject", asset: "sheep", label: "CharLose", tier: "Curiosity", expect: "char_lose", value: "lead=character;char=95"),
|
|
Step("dg96", "assert", expect: "interest_peek_lead", value: "EventLed"),
|
|
// Empty event pool → character-led, no invented events
|
|
Step("dg97", "interest_expire_pending", value: "evt_win"),
|
|
Step("dg98", "assert", expect: "interest_peek_lead", value: "CharacterLed"),
|
|
|
|
// --- Event strength beats idle celebrity (within event pool / score order) ---
|
|
Step("dg100", "interest_expire_pending", value: ""),
|
|
Step("dg101", "interest_inject", asset: "sheep", label: "StrongEvt", tier: "Action", expect: "strong_evt", value: "lead=event;evt=95;char=5"),
|
|
Step("dg102", "interest_inject", asset: "sheep", label: "CelebWeak", tier: "Action", expect: "celeb_weak", value: "lead=event;evt=10;char=99"),
|
|
Step("dg103", "assert", expect: "interest_score_order", value: "strong_evt", label: "celeb_weak"),
|
|
|
|
// --- Ambient happiness never owns camera; psychopath suppress; drain duplex; aggregate ---
|
|
Step("dg110", "interest_force_session", asset: "sheep", label: "ActionKeep", tier: "Action", expect: "action_keep"),
|
|
Step("dg111", "happiness_apply", value: "just_ate"),
|
|
Step("dg112", "interest_feeds_tick"),
|
|
Step("dg113", "age_current", wait: 1.2f),
|
|
Step("dg114", "director_run", wait: 1.2f),
|
|
Step("dg115", "assert", expect: "current_tier", value: "Action"),
|
|
Step("dg116", "assert", expect: "tip_contains", value: "ActionKeep"),
|
|
Step("dg117", "assert", expect: "interest_no_key", value: "just_ate"),
|
|
|
|
Step("dg120", "happiness_suppress", value: "death_child"),
|
|
Step("dg121", "interest_feeds_tick"),
|
|
Step("dg122", "assert", expect: "interest_no_key", value: "death_child"),
|
|
|
|
Step("dg130", "happiness_reset"),
|
|
Step("dg131", "happiness_remember_only", value: "death_lover", label: "Ava"),
|
|
Step("dg132", "interest_feeds_tick"),
|
|
Step("dg133", "assert", expect: "interest_has_key", value: "death_lover"),
|
|
Step("dg134", "assert", expect: "interest_registry_count", value: "1", label: "death_lover"),
|
|
Step("dg135", "interest_feeds_tick"),
|
|
Step("dg136", "assert", expect: "interest_registry_count", value: "1", label: "death_lover"),
|
|
|
|
Step("dg140", "happiness_burst", value: "just_started_war", count: 32),
|
|
Step("dg141", "interest_feeds_tick"),
|
|
Step("dg142", "assert", expect: "interest_no_key", value: "just_started_war"),
|
|
Step("dg143", "interest_civic_boost", value: "just_started_war", label: "harness", expect: "40"),
|
|
Step("dg144", "assert", expect: "civic_boost", value: "just_started_war"),
|
|
|
|
Step("dg190", "assert", expect: "no_bad"),
|
|
Step("dg191", "fast_timing", value: "false"),
|
|
Step("dg199", "snapshot"),
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Watch-reason row must explain why the camera is here (event), not who (title/name).
|
|
/// </summary>
|
|
private static List<HarnessCommand> WatchReasonClarity()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("wr0", "dismiss_windows"),
|
|
Step("wr1", "wait_world"),
|
|
Step("wr2", "set_setting", expect: "enabled", value: "true"),
|
|
Step("wr3", "set_setting", expect: "show_watch_reasons", value: "true"),
|
|
Step("wr4", "set_setting", expect: "show_dossier_caption", value: "true"),
|
|
Step("wr5", "spawn", asset: "human"),
|
|
Step("wr6", "spectator", value: "off"),
|
|
Step("wr7", "spectator", value: "on"),
|
|
Step("wr8", "focus", asset: "human"),
|
|
Step("wr9", "dossier_clear_job"),
|
|
|
|
// Fighting label must surface as Fighting - not "Action · King …".
|
|
Step("wr10", "interest_force_session", asset: "human", label: "Fighting: Isemward", tier: "Action", expect: "fight_reason"),
|
|
Step("wr11", "assert", expect: "dossier_contains", value: "Fighting"),
|
|
Step("wr12", "assert", expect: "dossier_not_contains", value: "Action ·"),
|
|
Step("wr13", "assert", expect: "tip_contains", value: "Fighting"),
|
|
Step("wr14", "screenshot", value: "hud-watch-reason-fighting.png"),
|
|
Step("wr15", "wait", wait: 0.55f),
|
|
|
|
// Stale identity label must not become "Action · King Name of Realm".
|
|
Step("wr20", "interest_force_session", asset: "human", label: "King Isemward of Essionan", tier: "Action", expect: "king_identity"),
|
|
Step("wr21", "assert", expect: "dossier_not_contains", value: "Action ·"),
|
|
Step("wr22", "assert", expect: "dossier_not_contains", value: "King Isemward"),
|
|
Step("wr23", "screenshot", value: "hud-watch-reason-identity.png"),
|
|
Step("wr24", "wait", wait: 0.55f),
|
|
|
|
// WorldLog-style event phrase stays clear.
|
|
Step("wr30", "interest_force_session", asset: "human", label: "War: Essionan vs North", tier: "Epic", expect: "war_reason"),
|
|
Step("wr31", "assert", expect: "dossier_contains", value: "War"),
|
|
Step("wr32", "assert", expect: "dossier_not_contains", value: "Action ·"),
|
|
Step("wr33", "screenshot", value: "hud-watch-reason-war.png"),
|
|
Step("wr34", "wait", wait: 0.55f),
|
|
|
|
Step("wr90", "assert", expect: "no_bad"),
|
|
Step("wr99", "snapshot"),
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Actions outrank characters; same action → more important character wins;
|
|
/// multi-fighter battles outrank anonymous 1v1; notable duels can beat nameless melees.
|
|
/// </summary>
|
|
private static List<HarnessCommand> DirectorActionPriority()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("ap0", "dismiss_windows"),
|
|
Step("ap1", "wait_world"),
|
|
Step("ap2", "set_setting", expect: "enabled", value: "true"),
|
|
Step("ap3", "fast_timing", value: "true"),
|
|
Step("ap4", "spawn", asset: "sheep", count: 2),
|
|
Step("ap5", "spectator", value: "off"),
|
|
Step("ap6", "spectator", value: "on"),
|
|
Step("ap7", "focus", asset: "sheep"),
|
|
Step("ap8", "interest_variety_clear"),
|
|
Step("ap9", "interest_expire_pending", value: ""),
|
|
|
|
// Same action intensity: higher character significance wins.
|
|
Step("ap10", "interest_inject", asset: "sheep", label: "FightKing", tier: "Action", expect: "fight_king",
|
|
value: "lead=event;evt=70;char=40;fighters=2;notables=1"),
|
|
Step("ap11", "interest_inject", asset: "sheep", label: "FightPeasant", tier: "Action", expect: "fight_peasant",
|
|
value: "lead=event;evt=70;char=5;fighters=2;notables=0"),
|
|
Step("ap12", "assert", expect: "interest_score_order", value: "fight_king", label: "fight_peasant"),
|
|
|
|
// Multi-person anonymous melee beats anonymous 1v1 at same base evt.
|
|
Step("ap20", "interest_expire_pending", value: ""),
|
|
Step("ap21", "interest_inject", asset: "sheep", label: "Melee", tier: "Action", expect: "melee_mass",
|
|
value: "lead=event;evt=55;char=5;fighters=8;notables=0"),
|
|
Step("ap22", "interest_inject", asset: "sheep", label: "DuelAnon", tier: "Action", expect: "duel_anon",
|
|
value: "lead=event;evt=55;char=5;fighters=2;notables=0"),
|
|
Step("ap23", "assert", expect: "interest_score_order", value: "melee_mass", label: "duel_anon"),
|
|
|
|
// Notable duel can beat nameless melee.
|
|
Step("ap30", "interest_expire_pending", value: ""),
|
|
Step("ap31", "interest_inject", asset: "sheep", label: "MeleeNoName", tier: "Action", expect: "melee_noname",
|
|
value: "lead=event;evt=55;char=5;fighters=8;notables=0"),
|
|
Step("ap32", "interest_inject", asset: "sheep", label: "DuelKings", tier: "Action", expect: "duel_kings",
|
|
value: "lead=event;evt=55;char=40;fighters=2;notables=2"),
|
|
Step("ap33", "assert", expect: "interest_score_order", value: "duel_kings", label: "melee_noname"),
|
|
|
|
// Combat Action cuts celebrity Story vignette after settle.
|
|
Step("ap40", "interest_expire_pending", value: ""),
|
|
Step("ap41", "interest_force_session", asset: "sheep", label: "KingStroll", tier: "Story", expect: "king_stroll"),
|
|
// Mark current as character vignette (not WorldLog story event).
|
|
Step("ap42", "interest_inject", asset: "sheep", label: "RealFight", tier: "Action", expect: "real_fight",
|
|
value: "lead=event;evt=90;char=10;fighters=4;notables=1;force=true"),
|
|
Step("ap43", "age_current", wait: 1.2f),
|
|
Step("ap44", "director_run", wait: 1.2f),
|
|
Step("ap45", "assert", expect: "tip_contains", value: "RealFight"),
|
|
Step("ap46", "assert", expect: "current_tier", value: "Action"),
|
|
|
|
Step("ap90", "assert", expect: "no_bad"),
|
|
Step("ap91", "fast_timing", value: "false"),
|
|
Step("ap99", "snapshot"),
|
|
};
|
|
}
|
|
|
|
private static List<HarnessCommand> Discovery()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("d0", "dismiss_windows"),
|
|
Step("d1", "wait_world"),
|
|
Step("d2", "set_setting", expect: "enabled", value: "true"),
|
|
Step("d3", "fast_timing", value: "true"),
|
|
Step("d4", "discovery_reset"),
|
|
// Calm animal focus so Action-tier combat does not block curiosity drain.
|
|
Step("d5", "spawn", asset: "sheep"),
|
|
Step("d6", "spectator", value: "off"),
|
|
Step("d7", "spectator", value: "on"),
|
|
Step("d8", "focus", asset: "sheep"),
|
|
Step("d9", "reset_counters"),
|
|
|
|
// Live-path dedupe: mark presented drops buffered copy.
|
|
Step("d10", "discovery_note", asset: "sheep"),
|
|
Step("d11", "assert", expect: "pending_discovery", value: "1"),
|
|
Step("d12", "discovery_mark", asset: "sheep"),
|
|
Step("d13", "assert", expect: "pending_discovery", value: "0"),
|
|
Step("d14", "assert", expect: "presented", asset: "sheep", value: "true"),
|
|
|
|
// Force drain path once curiosity is acceptable.
|
|
Step("d20", "discovery_reset"),
|
|
Step("d21", "discovery_note", asset: "sheep"),
|
|
Step("d22", "assert", expect: "pending_discovery", value: "1"),
|
|
Step("d23", "age_current", wait: 2f),
|
|
Step("d24", "assert", expect: "would_accept_curiosity", value: "true"),
|
|
Step("d25", "discovery_drain"),
|
|
Step("d26", "assert", expect: "pending_discovery_asset", value: "false"),
|
|
Step("d27", "assert", expect: "presented", asset: "sheep", value: "true"),
|
|
Step("d28", "director_run", wait: 1.2f),
|
|
Step("d29", "assert", expect: "tip_matches_unit"),
|
|
Step("d30", "assert", expect: "no_bad"),
|
|
|
|
Step("d90", "fast_timing", value: "false"),
|
|
Step("d99", "snapshot"),
|
|
};
|
|
}
|
|
|
|
private static List<HarnessCommand> WorldLog()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("wl0", "dismiss_windows"),
|
|
Step("wl1", "wait_world"),
|
|
Step("wl2", "set_setting", expect: "enabled", value: "true"),
|
|
Step("wl3", "fast_timing", value: "true"),
|
|
Step("wl4", "pick_unit", asset: "auto"),
|
|
Step("wl5", "spectator", value: "off"),
|
|
Step("wl6", "spectator", value: "on"),
|
|
Step("wl7", "focus", asset: "auto"),
|
|
Step("wl8", "reset_counters"),
|
|
|
|
Step("wl10", "trigger_interest", asset: "auto", label: "Story: kingdom_new", tier: "Story"),
|
|
Step("wl11", "age_current", wait: 1.2f),
|
|
Step("wl12", "director_run", wait: 1.2f),
|
|
Step("wl13", "assert", expect: "current_tier", value: "Story"),
|
|
Step("wl14", "assert", expect: "tip_contains", value: "kingdom_new"),
|
|
|
|
Step("wl20", "trigger_interest", asset: "auto", label: "Epic: diplomacy_war_started", tier: "Epic"),
|
|
Step("wl21", "age_current", wait: 1.2f),
|
|
Step("wl22", "director_run", wait: 1.2f),
|
|
Step("wl23", "assert", expect: "current_tier", value: "Epic"),
|
|
Step("wl24", "assert", expect: "tip_contains", value: "diplomacy_war_started"),
|
|
Step("wl25", "assert", expect: "health"),
|
|
Step("wl26", "assert", expect: "no_bad"),
|
|
|
|
Step("wl90", "fast_timing", value: "false"),
|
|
Step("wl99", "snapshot"),
|
|
};
|
|
}
|
|
|
|
private static List<HarnessCommand> ActivityRateSample()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("ar0", "wait_world"),
|
|
Step("ar1", "activity_sample_reset"),
|
|
Step("ar2", "wait", wait: 20f),
|
|
Step("ar3", "activity_stats"),
|
|
Step("ar4", "snapshot"),
|
|
};
|
|
}
|
|
|
|
private static List<HarnessCommand> ActivityIdleSmoke()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("act0", "dismiss_windows"),
|
|
Step("act1", "wait_world"),
|
|
Step("act2", "set_setting", expect: "enabled", value: "true"),
|
|
Step("act3", "set_setting", expect: "show_dossier_caption", value: "true"),
|
|
Step("act4", "set_setting", expect: "chronicle_enabled", value: "true"),
|
|
Step("act5", "chronicle_clear"),
|
|
Step("act6", "activity_clear"),
|
|
Step("act7", "pick_unit", asset: "auto"),
|
|
Step("act8", "spectator", value: "off"),
|
|
Step("act9", "spectator", value: "on"),
|
|
Step("act10", "focus", asset: "auto"),
|
|
Step("act11", "wait", wait: 0.35f),
|
|
Step("act12", "activity_probe"),
|
|
Step("act13", "assert", expect: "activity_first_scoring"),
|
|
Step("act13b", "assert", expect: "activity_prose_sentence"),
|
|
Step("act13b2", "assert", expect: "activity_prose_alive"),
|
|
Step("act13c", "assert", expect: "activity_prose_species"),
|
|
Step("act13d", "assert", expect: "activity_prose_grounding"),
|
|
Step("act13e", "assert", expect: "activity_prose_library"),
|
|
Step("act13f", "assert", expect: "activity_taxonomy_audit"),
|
|
Step("act13g", "assert", expect: "activity_status_audit"),
|
|
Step("act13h", "assert", expect: "activity_verb_dedupe"),
|
|
Step("act13h2", "assert", expect: "activity_verb_exempt"),
|
|
Step("act13i", "assert", expect: "dossier_task_refresh"),
|
|
Step("act14", "activity_force", asset: "laughing", label: "Laughing", count: 1, value: "angle"),
|
|
Step("act14b", "assert", expect: "activity_log_contains", value: "celestial"),
|
|
Step("act14c", "activity_force", asset: "eating", label: "Taking a meal", count: 1),
|
|
Step("act15", "activity_force", asset: "farm", label: "Tends the fields", count: 1),
|
|
Step("act16", "activity_force", asset: "hunt", label: "Hunts", count: 1, expect: "Barkley"),
|
|
Step("act16b", "activity_force", asset: "task_unit_play", label: "Playing", value: "dog"),
|
|
Step("act16c", "assert", expect: "activity_log_contains", value: "bounds"),
|
|
Step("act16d", "activity_force", asset: "task_unit_play", label: "Playing", value: "cat"),
|
|
Step("act16e", "assert", expect: "activity_prose_variety", value: "2"),
|
|
Step("act16f", "activity_force", asset: "BehUnloadResources", label: "wheat@Riverhold", tier: "beat", value: "human"),
|
|
Step("act16g", "assert", expect: "activity_log_contains", value: "wheat"),
|
|
Step("act16g2", "assert", expect: "activity_log_contains", value: "Riverhold"),
|
|
Step("act16g3", "assert", expect: "activity_log_not_contains", value: "while carrying"),
|
|
Step("act16h", "activity_force", asset: "BehSocializeTalk", label: "Talks", count: 1, tier: "beat", expect: "Barkley"),
|
|
Step("act16i", "assert", expect: "activity_log_contains", value: "Barkley"),
|
|
Step("act16i2", "assert", expect: "activity_log_not_contains", value: "with another"),
|
|
// JobLabel on dossier reason row (deterministic harness force).
|
|
Step("act16j", "dossier_force_job", value: "farmer"),
|
|
Step("act16k", "assert", expect: "dossier_contains", value: "farmer"),
|
|
Step("act16l", "screenshot", value: "hud-dossier-job-only.png"),
|
|
Step("act16m", "wait", wait: 0.45f),
|
|
Step("act16n", "dossier_force_job", value: "farmer", label: "Story"),
|
|
Step("act16o", "assert", expect: "dossier_contains", value: "Story · farmer"),
|
|
Step("act16p", "screenshot", value: "hud-dossier-reason-job.png"),
|
|
Step("act16q", "wait", wait: 0.45f),
|
|
Step("act16r", "dossier_clear_job"),
|
|
Step("act17", "activity_force", asset: "BehPollinate", label: "Works the flower", count: 3, tier: "beat"),
|
|
Step("act17b", "activity_force", asset: "zzz_harness_act", label: "Harness pollen trail", count: 1),
|
|
Step("act18", "wait", wait: 0.25f),
|
|
Step("act19", "assert", expect: "activity_count", value: "4", label: "min"),
|
|
Step("act20", "assert", expect: "activity_prose_variety", value: "2"),
|
|
Step("act20b", "assert", expect: "activity_names_colored"),
|
|
Step("act20c", "assert", expect: "activity_log_contains", value: "Barkley"),
|
|
// Life milestones mirror into Activity (kill + Became lovers) while Life stays on Chronicle.
|
|
Step("act20d", "chronicle_milestone", value: "kill"),
|
|
Step("act20e", "assert", expect: "activity_log_contains", value: "killed"),
|
|
Step("act20f", "assert", expect: "chronicle_latest_contains", value: "Killed"),
|
|
Step("act20g", "chronicle_milestone", value: "lover"),
|
|
Step("act20h", "assert", expect: "activity_log_contains", value: "Became lovers"),
|
|
Step("act20i", "assert", expect: "chronicle_latest_contains", value: "Became lovers"),
|
|
Step("act21", "assert", expect: "dossier_history_shown", value: "1", label: "min", tier: "activity"),
|
|
Step("act22", "assert", expect: "dossier_history_not_contains", value: "Harness pollen"),
|
|
Step("act23", "assert", expect: "caption_layout_ok"),
|
|
Step("act24", "screenshot", value: "hud-activity_idle_smoke.png"),
|
|
Step("act25", "wait", wait: 0.55f),
|
|
// Life still peeks when chronicle history exists beside activity.
|
|
Step("act26", "chronicle_force", label: "Killed a serpent"),
|
|
Step("act27", "wait", wait: 0.2f),
|
|
Step("act28", "assert", expect: "dossier_history_contains", value: "serpent"),
|
|
Step("act29", "assert", expect: "dossier_history_shown", value: "1", label: "min", tier: "life"),
|
|
Step("act29b", "screenshot", value: "hud-activity-life-split.png"),
|
|
Step("act29c", "wait", wait: 0.45f),
|
|
// Mixed activity icons + kill/lover milestones in dossier peek.
|
|
Step("act29d", "screenshot", value: "hud-activity-icons-milestones.png"),
|
|
Step("act29e", "wait", wait: 0.45f),
|
|
// Books: Activity / Life tabs.
|
|
Step("act40", "lore_open_focus"),
|
|
Step("act41", "wait", wait: 0.25f),
|
|
Step("act42", "assert", expect: "lore_detail_pane", value: "life"),
|
|
Step("act43", "lore_detail_pane", value: "activity"),
|
|
Step("act44", "wait", wait: 0.2f),
|
|
Step("act45", "assert", expect: "lore_detail_pane", value: "activity"),
|
|
Step("act46", "assert", expect: "lore_activity_shown", value: "4", label: "min"),
|
|
Step("act47", "screenshot", value: "hud-lore-activity-tab.png"),
|
|
Step("act48", "wait", wait: 0.45f),
|
|
Step("act49", "lore_detail_pane", value: "life"),
|
|
Step("act50", "assert", expect: "lore_history_shown", value: "1", label: "min"),
|
|
Step("act51", "assert", expect: "idle", value: "false"),
|
|
Step("act52", "assert", expect: "caption_layout_ok"),
|
|
Step("act53", "assert", expect: "no_bad"),
|
|
Step("act99", "snapshot"),
|
|
};
|
|
}
|
|
|
|
private static List<HarnessCommand> ChronicleSmoke()
|
|
{
|
|
return new List<HarnessCommand>
|
|
{
|
|
Step("ch0", "dismiss_windows"),
|
|
Step("ch1", "wait_world"),
|
|
Step("ch2", "set_setting", expect: "enabled", value: "true"),
|
|
Step("ch3", "set_setting", expect: "show_dossier_caption", value: "true"),
|
|
Step("ch4", "set_setting", expect: "chronicle_enabled", value: "true"),
|
|
Step("ch4b", "chronicle_clear"),
|
|
Step("ch5", "pick_unit", asset: "auto"),
|
|
Step("ch6", "spectator", value: "off"),
|
|
Step("ch7", "spectator", value: "on"),
|
|
Step("ch8", "focus", asset: "auto"),
|
|
Step("ch9", "wait", wait: 0.35f),
|
|
Step("ch10", "assert", expect: "health"),
|
|
Step("ch11", "assert", expect: "dossier_contains", value: "auto"),
|
|
Step("ch12", "assert", expect: "caption_contains", value: "auto"),
|
|
Step("ch12b", "assert", expect: "caption_layout_ok"),
|
|
Step("ch12c", "assert", expect: "dossier_traits_ok"),
|
|
Step("ch12d", "screenshot", value: "hud-dossier-empty-hist.png"),
|
|
Step("ch12e", "wait", wait: 0.55f),
|
|
|
|
// History inject
|
|
Step("ch13", "chronicle_force", label: "Killed"),
|
|
Step("ch14", "assert", expect: "chronicle_count", value: "1", label: "min"),
|
|
Step("ch15", "assert", expect: "chronicle_latest_contains", value: "Killed"),
|
|
Step("ch15a2", "assert", expect: "chronicle_latest_dated"),
|
|
Step("ch15b", "assert", expect: "chronicle_latest_contains", value: "auto"),
|
|
Step("ch15c", "wait", wait: 0.2f),
|
|
Step("ch15d", "assert", expect: "dossier_history_contains", value: "Killed"),
|
|
// Real kill / lover milestones also land in Activity.
|
|
Step("ch15d2", "chronicle_milestone", value: "kill"),
|
|
Step("ch15d3", "assert", expect: "activity_log_contains", value: "killed"),
|
|
Step("ch15d4", "assert", expect: "chronicle_latest_contains", value: "Killed"),
|
|
Step("ch15d5", "chronicle_milestone", value: "lover"),
|
|
Step("ch15d6", "assert", expect: "activity_log_contains", value: "Became lovers"),
|
|
Step("ch15d7", "wait", wait: 0.2f),
|
|
Step("ch15d8", "assert", expect: "dossier_history_shown", value: "1", label: "min", tier: "activity"),
|
|
|
|
// Long event wraps in the narrow dossier history column (no wider panel).
|
|
Step("ch15e", "chronicle_force",
|
|
label: "Became legendary after surviving the great crab migration across three kingdoms, returning home with a stolen crown, and telling the tale for forty winters"),
|
|
Step("ch15f", "wait", wait: 0.35f),
|
|
Step("ch15g", "assert", expect: "dossier_history_contains", value: "crab migration"),
|
|
Step("ch15h", "assert", expect: "caption_layout_ok"),
|
|
Step("ch15i", "screenshot", value: "hud-dossier-long-hist.png"),
|
|
Step("ch15j", "wait", wait: 0.65f),
|
|
|
|
// Death-cause wording samples (victim POV)
|
|
Step("ch16a", "chronicle_death_sample", value: "Age"),
|
|
Step("ch16b", "assert", expect: "chronicle_latest_contains", value: "old age"),
|
|
Step("ch16c", "chronicle_death_sample", value: "Starvation"),
|
|
Step("ch16d", "assert", expect: "chronicle_latest_contains", value: "Starved"),
|
|
Step("ch16e", "chronicle_death_sample", value: "Drowning"),
|
|
Step("ch16f", "assert", expect: "chronicle_latest_contains", value: "Drown"),
|
|
Step("ch16g", "chronicle_death_sample", value: "Fire"),
|
|
Step("ch16h", "assert", expect: "chronicle_latest_contains", value: "Burned"),
|
|
// Character kills/deaths must stay on character history, not World Memory.
|
|
Step("ch16i", "assert", expect: "world_memory_world_only"),
|
|
// Soft-cap prune: temporarily lower the cap, flood past it, then restore.
|
|
Step("ch16j0", "chronicle_set_subject_cap", value: "500"),
|
|
Step("ch16j", "chronicle_orphan_flood", count: 580),
|
|
Step("ch16k", "assert", expect: "chronicle_subjects_capped"),
|
|
Step(
|
|
"ch16k2",
|
|
"chronicle_set_subject_cap",
|
|
value: Chronicle.DefaultMaxHistorySubjects.ToString()),
|
|
|
|
// World Memory landmarks (replaces History|World tabs)
|
|
Step("ch17a", "chronicle_world_force", label: "War: harness_alpha vs harness_beta"),
|
|
Step("ch17b", "assert", expect: "chronicle_count", value: "1", label: "world"),
|
|
Step("ch17c", "assert", expect: "world_memory_contains", value: "harness_alpha"),
|
|
Step("ch17d", "assert", expect: "chronicle_latest_contains", value: "Burned"),
|
|
Step("ch17e", "assert", expect: "world_memory_age"),
|
|
Step("ch17e2", "assert", expect: "world_memory_world_only"),
|
|
Step("ch17e3", "assert", expect: "chronicle_memory_capped"),
|
|
Step("ch17e4", "assert", expect: "chronicle_subjects_capped"),
|
|
// Selecting a world event pans to its recorded place (even with no unit there).
|
|
Step("ch17j", "lore_jump_world"),
|
|
Step("ch17k", "wait", wait: 0.2f),
|
|
Step("ch17l", "assert", expect: "world_jump_place"),
|
|
Step("ch17m", "assert", expect: "focus", value: "false"),
|
|
// Restore a living focus for the rest of the scenario.
|
|
Step("ch17n", "focus", asset: "auto"),
|
|
Step("ch17o", "wait", wait: 0.2f),
|
|
Step("ch17p", "assert", expect: "focus", value: "true"),
|
|
|
|
// World Memory panel + dossier controls
|
|
Step("ch18", "chronicle_open"),
|
|
Step("ch18a", "screenshot", value: "hud-chronicle_smoke.png"),
|
|
Step("ch18a2", "wait", wait: 0.6f),
|
|
Step("ch18b", "assert", expect: "world_memory_compact"),
|
|
Step("ch18b2", "assert", expect: "hud_no_overlap"),
|
|
Step("ch18c", "assert", expect: "caption_layout_ok"),
|
|
Step("ch18c2", "assert", expect: "dossier_traits_ok"),
|
|
Step("ch18d", "assert", expect: "world_memory_contains", value: "harness_alpha"),
|
|
Step("ch18e", "dossier_favorite", value: "true"),
|
|
Step("ch18f", "assert", expect: "is_favorite", value: "true"),
|
|
Step("ch18g", "dossier_favorite", value: "false"),
|
|
Step("ch18h", "assert", expect: "is_favorite", value: "false"),
|
|
// Dossier peeks 3 lines; books button opens full history in Lore and pauses idle.
|
|
Step("ch18h1", "chronicle_force", label: "Hist", count: 40),
|
|
Step("ch18h2", "wait", wait: 0.25f),
|
|
Step("ch18k", "assert", expect: "dossier_history_shown", value: "3", label: "exact"),
|
|
Step("ch18k3", "assert", expect: "dossier_history_fills_body"),
|
|
Step("ch18l", "assert", expect: "caption_layout_ok"),
|
|
Step("ch18l1", "assert", expect: "hud_no_overlap"),
|
|
Step("ch18l2", "screenshot", value: "hud-dossier-peek.png"),
|
|
Step("ch18l3", "wait", wait: 0.45f),
|
|
// L shortcut syncs to the focused dossier unit (Follow mode) and locks idle.
|
|
Step("ch18m", "lore_open_focus"),
|
|
Step("ch18n", "wait", wait: 0.3f),
|
|
Step("ch18o", "assert", expect: "idle", value: "false"),
|
|
Step("ch18o1", "lore_tab", value: "living"),
|
|
Step("ch18o1b", "wait", wait: 0.15f),
|
|
Step("ch18o2", "assert", expect: "lore_tab", value: "living"),
|
|
Step("ch18o3", "assert", expect: "lore_detail", value: "true"),
|
|
Step("ch18o3b", "assert", expect: "lore_follow", value: "true"),
|
|
Step("ch18o4", "assert", expect: "lore_history_shown", value: "40", label: "min"),
|
|
Step("ch18o4b", "assert", expect: "dossier_lore_history_match"),
|
|
Step("ch18o4c", "assert", expect: "dossier_pinned", value: "true"),
|
|
Step("ch18o4d", "assert", expect: "lore_zoom_blocked"),
|
|
Step("ch18o5", "screenshot", value: "hud-lore-unit-history.png"),
|
|
Step("ch18o6", "wait", wait: 0.55f),
|
|
// Banner used to expire at 5s and hide the dossier - pin must outlive that.
|
|
Step("ch18o7", "wait", wait: 5.5f),
|
|
Step("ch18o8", "assert", expect: "dossier_pinned", value: "true"),
|
|
Step("ch18o9", "assert", expect: "idle", value: "false"),
|
|
Step("ch18o10", "assert", expect: "dossier_lore_history_match"),
|
|
Step("ch18p", "assert", expect: "hud_no_overlap"),
|
|
// Resume idle: Follow mode Lore must retarget when dossier focus changes.
|
|
Step("ch18f1", "spectator", value: "on"),
|
|
Step("ch18f2", "wait", wait: 0.2f),
|
|
Step("ch18f3", "spawn", asset: "rabbit", count: 1),
|
|
Step("ch18f4", "focus", asset: "auto"),
|
|
Step("ch18f5", "wait", wait: 0.35f),
|
|
Step("ch18f6", "assert", expect: "lore_follows_focus"),
|
|
Step("ch18f7", "assert", expect: "lore_follow", value: "true"),
|
|
// Books button still opens Follow mode.
|
|
Step("ch18p0", "lore_close"),
|
|
Step("ch18p0b", "dossier_history_open"),
|
|
Step("ch18p0c", "wait", wait: 0.2f),
|
|
Step("ch18p0d", "assert", expect: "dossier_lore_history_match"),
|
|
Step("ch18p0e", "assert", expect: "idle", value: "false"),
|
|
Step("ch18p0f", "assert", expect: "lore_follow", value: "true"),
|
|
// Empty-history subject must not keep a previous unit's peek lines.
|
|
Step("ch18p1", "lore_back"),
|
|
Step("ch18p2", "spawn", asset: "cow", count: 1),
|
|
Step("ch18p3", "focus", asset: "auto"),
|
|
Step("ch18p4", "wait", wait: 0.25f),
|
|
Step("ch18p5", "dossier_history_open"),
|
|
Step("ch18p6", "wait", wait: 0.25f),
|
|
Step("ch18p7", "assert", expect: "idle", value: "false"),
|
|
Step("ch18p8", "assert", expect: "lore_detail", value: "true"),
|
|
// Fresh spawn still gets just_born Life; empty History UI is gone - expect birth biography.
|
|
Step("ch18p9", "assert", expect: "dossier_history_shown", value: "1", label: "min", tier: "life"),
|
|
Step("ch18p10", "assert", expect: "lore_history_shown", value: "1", label: "min"),
|
|
Step("ch18p11", "assert", expect: "dossier_lore_history_match"),
|
|
Step("ch18p12", "screenshot", value: "hud-lore-empty-history.png"),
|
|
Step("ch18p13", "wait", wait: 0.55f),
|
|
// Fallen (dead) archive: pinned; old age → death place.
|
|
Step("ch18d1", "lore_back"),
|
|
Step("ch18d1b", "focus", asset: "auto"),
|
|
Step("ch18d1c", "wait", wait: 0.2f),
|
|
Step(
|
|
"ch18d2",
|
|
"chronicle_orphan",
|
|
label: "Oldbone",
|
|
asset: "skeleton",
|
|
value: "Fallen",
|
|
count: 5,
|
|
expect: "Age",
|
|
tier: "place"),
|
|
Step("ch18d3", "lore_pick_fallen", value: "Oldbone"),
|
|
Step("ch18d4", "wait", wait: 0.25f),
|
|
Step("ch18d5", "assert", expect: "lore_detail", value: "true"),
|
|
Step("ch18d6", "assert", expect: "lore_follow", value: "false"),
|
|
Step("ch18d7", "assert", expect: "lore_history_shown", value: "5", label: "exact"),
|
|
Step("ch18d7b", "assert", expect: "fallen_focus", value: "place"),
|
|
Step("ch18d7c", "assert", expect: "death_manner", value: "old age"),
|
|
Step("ch18d7d", "assert", expect: "dossier_contains", value: "Oldbone"),
|
|
Step("ch18d7e", "assert", expect: "dossier_unit_matches_lore"),
|
|
Step("ch18d7f", "assert", expect: "dossier_species", value: "skeleton"),
|
|
Step("ch18d7g", "assert", expect: "dossier_avatar_species"),
|
|
Step("ch18d7h", "assert", expect: "fallen_place_no_follow"),
|
|
Step("ch18d8", "screenshot", value: "hud-lore-fallen.png"),
|
|
Step("ch18d9", "wait", wait: 0.55f),
|
|
// Fallen slain → living killer focus.
|
|
Step("ch18k1", "lore_back"),
|
|
Step("ch18k2", "spawn", asset: "human", count: 1),
|
|
Step("ch18k3", "focus", asset: "auto"),
|
|
Step("ch18k4", "wait", wait: 0.25f),
|
|
Step(
|
|
"ch18k5",
|
|
"chronicle_orphan",
|
|
label: "Slainbone",
|
|
asset: "skeleton",
|
|
value: "Murder",
|
|
count: 3,
|
|
expect: "Weapon",
|
|
tier: "killer"),
|
|
Step("ch18k6", "lore_pick_fallen", value: "Slainbone"),
|
|
Step("ch18k7", "wait", wait: 0.25f),
|
|
Step("ch18k8", "assert", expect: "lore_detail", value: "true"),
|
|
Step("ch18k9", "assert", expect: "lore_follow", value: "false"),
|
|
Step("ch18k10", "assert", expect: "fallen_focus", value: "killer"),
|
|
Step("ch18k11", "assert", expect: "death_manner", value: "slain"),
|
|
Step("ch18k11b", "assert", expect: "dossier_contains", value: "Slainbone"),
|
|
Step("ch18k11c", "assert", expect: "dossier_unit_matches_lore"),
|
|
Step("ch18k11d", "assert", expect: "dossier_species", value: "skeleton"),
|
|
Step("ch18k11e", "assert", expect: "dossier_avatar_species"),
|
|
Step("ch18k11f", "assert", expect: "fallen_killer_camera"),
|
|
Step("ch18k12", "screenshot", value: "hud-lore-fallen-slain.png"),
|
|
Step("ch18k13", "wait", wait: 0.55f),
|
|
// Regression: fallen species portrait must not stick onto the next living dossier.
|
|
Step("ch18av1", "lore_back"),
|
|
Step("ch18av1b", "lore_close"),
|
|
Step("ch18av1c", "spectator", value: "on"),
|
|
Step("ch18av1d", "wait", wait: 0.2f),
|
|
Step("ch18av2", "spawn", asset: "human", count: 1),
|
|
Step("ch18av3", "focus", asset: "auto"),
|
|
Step("ch18av4", "wait", wait: 0.25f),
|
|
Step("ch18av5", "assert", expect: "dossier_matches_focus"),
|
|
Step("ch18av6", "assert", expect: "dossier_species", value: "human"),
|
|
Step("ch18av7", "assert", expect: "dossier_avatar_live"),
|
|
Step("ch18av8", "screenshot", value: "hud-dossier-after-fallen.png"),
|
|
Step("ch18av9", "wait", wait: 0.55f),
|
|
// Also verify list-pick living after a fallen species portrait.
|
|
Step("ch18av10", "lore_pick_fallen", value: "Oldbone"),
|
|
Step("ch18av11", "wait", wait: 0.2f),
|
|
Step("ch18av12", "assert", expect: "dossier_avatar_species"),
|
|
Step("ch18av13", "lore_back"),
|
|
Step("ch18av14", "lore_pick", value: "auto"),
|
|
Step("ch18av15", "wait", wait: 0.25f),
|
|
Step("ch18av16", "assert", expect: "dossier_matches_focus"),
|
|
Step("ch18av17", "assert", expect: "dossier_avatar_live"),
|
|
Step("ch18av18", "screenshot", value: "hud-dossier-after-fallen-pick.png"),
|
|
Step("ch18av19", "wait", wait: 0.55f),
|
|
// Character list browse (pinned archive) - Living tab.
|
|
Step("ch18q", "lore_tab", value: "living"),
|
|
Step("ch18q0", "lore_back"),
|
|
Step("ch18q2", "assert", expect: "lore_tab", value: "living"),
|
|
Step("ch18q3", "assert", expect: "lore_recent", value: "1", label: "min"),
|
|
Step("ch18q4", "lore_pick", value: "auto"),
|
|
Step("ch18q5", "wait", wait: 0.25f),
|
|
Step("ch18q6", "assert", expect: "idle", value: "false"),
|
|
Step("ch18q7", "assert", expect: "lore_detail", value: "true"),
|
|
Step("ch18q7b", "assert", expect: "lore_follow", value: "false"),
|
|
Step("ch18q7c", "assert", expect: "dossier_unit_matches_lore"),
|
|
Step("ch18q7d", "assert", expect: "dossier_matches_focus"),
|
|
Step("ch18q7e", "assert", expect: "dossier_avatar_live"),
|
|
Step("ch18q7f", "assert", expect: "lore_tab", value: "living"),
|
|
Step("ch18q8", "screenshot", value: "hud-lore-characters.png"),
|
|
Step("ch18q9", "wait", wait: 0.55f),
|
|
// Resume idle: pinned list pick flips back to Follow mode.
|
|
Step("ch18q10", "spectator", value: "on"),
|
|
Step("ch18q11", "wait", wait: 0.25f),
|
|
Step("ch18q11b", "assert", expect: "lore_follow", value: "true"),
|
|
Step("ch18q11c", "spawn", asset: "sheep", count: 1),
|
|
Step("ch18q11d", "focus", asset: "auto"),
|
|
Step("ch18q11e", "wait", wait: 0.35f),
|
|
Step("ch18q11f", "assert", expect: "lore_follows_focus"),
|
|
// Fallen tab: newest death first (Slainbone after Oldbone).
|
|
Step("ch18ft1", "lore_back"),
|
|
Step("ch18ft2", "lore_tab", value: "fallen"),
|
|
Step("ch18ft3", "wait", wait: 0.2f),
|
|
Step("ch18ft4", "assert", expect: "lore_tab", value: "fallen"),
|
|
Step("ch18ft5", "assert", expect: "fallen_list_before", value: "Slainbone", label: "Oldbone"),
|
|
// Snapshot stays put until Refresh (new deaths do not auto-rebuild).
|
|
Step("ch18ft5b", "assert", expect: "lore_list_stale", value: "false"),
|
|
Step(
|
|
"ch18ft5c",
|
|
"chronicle_orphan",
|
|
label: "FreshFallen",
|
|
asset: "human",
|
|
value: "Fresh",
|
|
count: 1,
|
|
expect: "Age",
|
|
tier: "place"),
|
|
Step("ch18ft5d", "assert", expect: "lore_list_stale", value: "true"),
|
|
Step("ch18ft5e", "assert", expect: "fallen_list_top_not", value: "FreshFallen"),
|
|
Step("ch18ft5f", "lore_refresh"),
|
|
Step("ch18ft5g", "assert", expect: "lore_list_stale", value: "false"),
|
|
Step("ch18ft5h", "assert", expect: "fallen_list_top", value: "FreshFallen"),
|
|
// Favorite filter on Fallen (same star control as Living).
|
|
Step(
|
|
"ch18ft5i",
|
|
"chronicle_orphan",
|
|
label: "FavFallen",
|
|
asset: "human",
|
|
value: "Fav",
|
|
count: 1,
|
|
expect: "Age",
|
|
tier: "place+fav"),
|
|
Step("ch18ft5j", "lore_favorites", value: "true"),
|
|
Step("ch18ft5k", "lore_refresh"),
|
|
Step("ch18ft5l", "assert", expect: "fallen_list_top", value: "FavFallen"),
|
|
Step("ch18ft5m", "lore_favorites", value: "false"),
|
|
Step("ch18ft5n", "lore_refresh"),
|
|
Step("ch18ft6", "screenshot", value: "hud-lore-fallen-tab.png"),
|
|
Step("ch18ft7", "wait", wait: 0.55f),
|
|
Step("ch18q12", "lore_tab", value: "world"),
|
|
Step("ch19", "assert", expect: "focus", value: "true"),
|
|
Step("ch20", "assert", expect: "health"),
|
|
Step("ch25", "assert", expect: "no_bad"),
|
|
Step("ch99", "snapshot"),
|
|
};
|
|
}
|
|
|
|
private static HarnessCommand Nested(string id, string scenario)
|
|
{
|
|
return Step(id, "scenario", value: scenario, expect: "keep");
|
|
}
|
|
|
|
private static HarnessCommand Step(
|
|
string id,
|
|
string action,
|
|
string value = "",
|
|
string asset = "",
|
|
string label = "",
|
|
string tier = "",
|
|
string expect = "",
|
|
int count = 1,
|
|
float wait = 0f)
|
|
{
|
|
return new HarnessCommand
|
|
{
|
|
id = id,
|
|
action = action,
|
|
value = value,
|
|
asset = asset,
|
|
label = label,
|
|
tier = tier,
|
|
expect = expect,
|
|
count = count,
|
|
wait = wait
|
|
};
|
|
}
|
|
}
|