Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/athena-sim/src/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ fn run(
return unsupported("setAbilityCooldown arguments");
};
// `Set Ability Cooldown` writes the reported cooldown for
// the slot literally; whether `abilityCooldown%` also
// scales it is what P7 is for.
// the slot literally; P7 measured that `abilityCooldown%`
// does not scale the write.
let ids = player_ids(player, &env)?;
let slot = button_slot(button)?;
let seconds =
Expand Down
7 changes: 3 additions & 4 deletions crates/athena-sim/tests/fixtures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,14 +244,13 @@ fn p6_wait_behavior() {
}

#[test]
#[ignore = "no captures yet (`runs` is 0)"]
fn p7_hero_settings() {
check_probe("p7-hero-settings");
}

/// P7 is `inconclusive`: it has no captures. This test does not assert
/// client behavior; it checks the simulator's modeled player state produces
/// the hypothesis lines in `expect.json`, and that a bare `run_text` — no
/// `p7_hero_settings` asserts the P7 client capture. This test does not
/// assert client behavior; it checks the simulator's modeled player state
/// produces the lines in `expect.json`, and that a bare `run_text` — no
/// scenario, so no player — reports the probe `not_modeled`. The values are
/// test-loaded so the check does not wait on a workshop-rs release.
#[test]
Expand Down
4 changes: 2 additions & 2 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ The simulator makes these choices only to stay deterministic; each needs a probe
- Comparing values of different types with `==` gives false; `Null` equals `0`. Two mocked players compare by scenario `id`.
- A mocked player is a scenario `id`; `Event Player` yields `{"type":"player"}`. `Hero Of` yields the catalog member id the declared `hero` resolves to; `Team Of` likewise yields the `Team` member id of the declared `team`, so it compares equal to a `Team` literal.
- An event rule fires when the triggering event's `kind` — injected or raised — matches the rule event's catalog id and the event player satisfies the rule's `team` and player (`target`) filters.
- **Modeled, not measured** — no client capture backs the player state table or the `Damage`/`Heal` rules:
- **Modeled, not measured** — probe P7 measured `health%`, `damageReceived%` and `healingReceived%` on scripted `Damage`/`Heal`, and a literal `Set Ability Cooldown` under `abilityCooldown%` 50 (Ana, client 2.25.0.1); nothing else backs the player state table or the `Damage`/`Heal` rules:
- Each scenario player has a state-table row built at run start from the `workshop-rs` hero-values file pinned at `CURRENT_VERSION`: a `health` pool, `shield` and `armor` pools where the catalog records them (a hero without the pool has no entry, never a zero), the cooldown seconds of the ability slots the catalog records (`slot` or `slot/variant` keys), and an ultimate charge of `0` toward the catalog's `ultimateCost`. Pools start at their maximum. The program's hero settings then apply: `health%` scales every pool, the `abilityCooldown%` and per-slot `*Cooldown%` scalars scale the reported cooldowns, and the dealt/received scalars feed `Damage`/`Heal` below. Percent scalars authored at several scopes multiply together. A hero the file does not supply, a player with no `hero`, or a hero setting authored for the player that the simulator does not apply (one that moves a reported value — ultimate generation, spawning with ultimate, passive health regeneration or the role passive, headshots-only, spawning without mech, or a cooldown scalar with no reported slot) leaves the row's `world` null; an action that touches that world state (`Damage`, `Heal`, dealt or received) stops the run as `not_modeled`. The exception is a player whose version-2 `state` map declares every `state` key the setting can move — `ultCharge` for the ultimate-charge settings (`ultGen%`, `passiveUltGen%`, `combatUltGen%`, `enableSpawningWithUlt`), `health`, `maxHealth` and `normalizedHealth` for the health ones (`passiveHealthRegen`, `enableRolePassive`): the scenario already supplies the value the setting could move, so the setting is skipped — `state.<key>` stands, the setting itself is not simulated (athena#91). The other unapplied settings have no `state` coverage and keep failing, and an opaque `Raw*` member fails the same way.
- `Health`/`Max Health` read the pools' summed `current`/`max` only while the player declares no version-2 `state` map — a declared `state.health`/`state.maxHealth` answers instead (athena#89); `Ability Cooldown` reads the slot's reported seconds (`0` where the player has no such slot); `Set Ability Cooldown` writes the slot's reported seconds literally — whether `abilityCooldown%` also scales a scripted set is what probe P7 is for. Reading a player's world state is a touch like `Damage`/`Heal`.
- `Health`/`Max Health` read the pools' summed `current`/`max` only while the player declares no version-2 `state` map — a declared `state.health`/`state.maxHealth` answers instead (athena#89); `Ability Cooldown` reads the slot's reported seconds (`0` where the player has no such slot); `Set Ability Cooldown` writes the slot's reported seconds literally; P7 measured that `abilityCooldown%` 50 does not scale a scripted set (20 stays 20). Natural ability cooldowns are not measured. Reading a player's world state is a touch like `Damage`/`Heal`.
- `Damage(player, damager, amount)` takes `amount` off the pools in shield, armor, health order: each pool absorbs up to its `current` and the rest spills into the next; health floors at zero and a negative amount does nothing. `Heal(player, healer, amount)` raises the `health` pool by `amount`, capped at `max` — it never exceeds the maximum, never refills shield or armor, and a negative amount does nothing. Both accept a `Player` or an array of players. The amount lands scaled: by the dealer's `damageDealt%` and the target's `damageReceived%` for `Damage`, and by the healer's `healingDealt%` and the target's `healingReceived%` for `Heal`; a `Null` dealer or healer carries no scale. A `damager`/`healer` value that is not a player or `Null` is not modeled.
- `Damage`/`Heal` act only on living players (athena#47): the one liveness read — a state cell or the declared `state.isDead`, the same read `Is Alive`/`Is Dead`/`Set Player Health` make, with the health pool deciding where `isDead` cannot answer — gates both, so a dead target's pools do not move and no events raise. A hit that lands raises `Player Took Damage` on the target and `Player Dealt Damage` on a player dealer; a hit that empties `health` writes the `isDead` cell and adds `Player Died` then `Player Dealt Final Blow`; a heal raises `Player Received Healing`/`Player Dealt Healing`. The events bind `Event Player` to the player they happened to and `Attacker`, `Victim`, `Event Damage`/`Event Healing` to the cause; an event value the cause does not bind is `not_modeled`. `Respawn` refills the health pool to `max` alongside its `isDead`/`health` writes — "with full health, even if they were already alive" — and `Resurrect` refills only when it revives a dead target, a living one keeping its health; either revive leaves a player that can die again.
- **Modeled, not measured** — spawn state and the host player. No capture covers them:
Expand Down
5 changes: 5 additions & 0 deletions probes/p7-hero-settings/capture-2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[00:00:18] P7 max_health=375
[00:00:18] P7 health_after_damage=275
[00:00:18] P7 health_after_heal=355
[00:00:18] P7 ability1_cooldown=20
[00:00:18] P7 done
5 changes: 5 additions & 0 deletions probes/p7-hero-settings/capture-3.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[00:00:19] P7 max_health=375
[00:00:19] P7 health_after_damage=275
[00:00:19] P7 health_after_heal=355
[00:00:19] P7 ability1_cooldown=20
[00:00:19] P7 done
30 changes: 23 additions & 7 deletions probes/p7-hero-settings/capture.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
{
"probe": "p7-hero-settings",
"client": {
"version": "",
"date": "",
"version": "2.25.0.1 - 154504.1",
"date": "2026-11-03",
"locale": "zh-CN"
},
"runs": 0,
"runs": 3,
"probe_sha256": "5aa48b5a6807880ed6531c58486c22d8ca44e56803965db8482cc386c138e504",
"hypothesis": "hero settings move the reported player state: health% 150 on Ana (health pool 250) reports Max Health 375; damageReceived% 50 lands a scripted Damage of 200 as 100; healingReceived% 200 lands a scripted Heal of 40 as 80; abilityCooldown% 50 applies to ability cooldowns, while Set Ability Cooldown(player, Ability 1, 20) is expected to write the timer literally and report 20",
"observed": [],
"result": "inconclusive",
"notes": "No captures yet; running the probe in the client is an owner step. The expected lines are hypotheses in expect.json. A cooldown reading of 10 instead of 20 would mean abilityCooldown% scales Set Ability Cooldown as well; either observation is a real result. The settings restrict the lobby to Ana (enabledHeroes), so any player produces the same numbers.",
"sources": []
"observed": [
{
"runs": [
1,
2,
3
],
"max_health": 375,
"health_after_damage": 275,
"health_after_heal": 355,
"ability1_cooldown_s": 20
}
],
"result": "match",
"notes": "All three runs gave the same four readings. Set Ability Cooldown wrote 20 literally, so abilityCooldown% 50 did not scale the scripted write. The probe measures script-applied damage, healing and cooldown writes, not natural ability cooldowns. The client version was read on 2026-11-03; the versions of runs 1 and 2 were not recorded. Balance version: workshop-rs names the client version of its hero-values file as `balanceVersion` (CURRENT_VERSION `2.25.0.0 - 154461.1`), not a named patch. This capture's client is 2.25.0.1 and workshop-rs has no file for it, so the balance version of the Ana 250 health reading is not determinable; the 375 reading only matches a 250 base. Raw logs were received through UU Remote file transfer.",
"sources": [
"Log-2026-10-27-18-29-27.txt",
"Log-2026-11-02-16-11-32.txt",
"Log-2026-11-03-01-53-07.txt"
]
}
5 changes: 5 additions & 0 deletions probes/p7-hero-settings/capture.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[00:00:19] P7 max_health=375
[00:00:19] P7 health_after_damage=275
[00:00:19] P7 health_after_heal=355
[00:00:19] P7 ability1_cooldown=20
[00:00:19] P7 done
Loading