The slam macro was never ours, and a fixture I invented is why I said otherwise
grom reports `/cast [noslamclip] Slam; [slamclip] Heroic Strike` never picking
Slam. Yesterday I drove the whole chain in the harness, measured a 44% no-clip
window and called it healthy. The 44% came from a cast time I supplied:
s.set_spell_tooltip(1464, … cast_time: Some("1.5 sec cast") …)
**OctoWoW's Slam is a 2.5 s cast.** `Spell.dbc` ids 1464/8820/11604/11605 all
carry `CastingTimeIndex` 20 and `SpellCastTimes.dbc` row 20 is 2500 ms, where
vanilla's Slam is 1500. SuperCleveRoidMacros' own
`DEFAULT_SLAM_CAST = 2.5 -- Turtle WoW` agrees independently. Against his 2.70 s
weapon the window is `(2.70 - 2.50) / 2.70` = 7.4%, 0.2 s wide — not something a
hand can hit, which is the whole report.
The same DBC gives every Slam rank `spellLevel` 54, and he is level 14. The
field alignment is checked against spells whose levels are known (Heroic Strike
rank 1/2/3 = 1/8/16, Charge 4, Shield Bash 12, Overpower 12, Thunder Clap 6),
so that reading is safe to act on: 477 Heroic Strikes in his combat log and zero
Slams, in any session since 10/12.
The fixtures now carry the realm's numbers and `WINDOW_PCT` carries where they
come from, including this mistake — a number I supply cannot test anything, and
a green test then argues against the player's own report.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
This commit is contained in:
@@ -24,6 +24,22 @@ use super::{
|
||||
};
|
||||
|
||||
/// Plates a crowded pull shows; the nameplate CVar cap and `pfValidUnits`' own range.
|
||||
/// `CleveRoids.GetSlamWindowPercent()` for grom's own numbers, as Lua prints it.
|
||||
///
|
||||
/// `(mainhand - slamCast) / mainhand * 100` = `(2.70 - 2.50) / 2.70 * 100`. **Both numbers are
|
||||
/// the realm's, not vanilla's.** Slam on OctoWoW is a 2.5 s cast — `Spell.dbc` id 1464 (and 8820,
|
||||
/// 11604, 11605) carries `CastingTimeIndex` 20, and `SpellCastTimes.dbc` row 20 is 2500 ms, where
|
||||
/// vanilla's Slam is 1.5 s. SuperCleveRoidMacros' own `DEFAULT_SLAM_CAST = 2.5 -- Turtle WoW`
|
||||
/// agrees independently.
|
||||
///
|
||||
/// So the no-clip window on a 2.70 s weapon is **0.2 s wide, 7.4% of the swing**, and a player
|
||||
/// pressing the macro by hand will almost never land in it. That is the macro's arithmetic, not
|
||||
/// a fault of this client — and it is worth a constant with this comment on it, because an
|
||||
/// earlier revision of these tests invented a `1.5 sec cast` fixture, measured a 44% window off
|
||||
/// it, and concluded the chain was healthy while the player's own sessions cast Heroic Strike 477
|
||||
/// times and Slam never once.
|
||||
const WINDOW_PCT: &str = "7.4074074074074";
|
||||
|
||||
const PLATES: u32 = 40;
|
||||
|
||||
/// Frames per timed window.
|
||||
@@ -1587,7 +1603,7 @@ fn the_whole_slam_macro_line_picks_slam_early_and_heroic_strike_late() {
|
||||
);
|
||||
assert_eq!(
|
||||
ask(&mut s, "CleveRoids.GetSlamCastTime()"),
|
||||
"1.5",
|
||||
"2.5",
|
||||
"the cast time came off the scanning tooltip, not the addon's 2.5 s fallback — against a \
|
||||
2.70 s swing the fallback leaves a 7% window instead of 44%, which in play reads as \
|
||||
`[noslamclip]` never being true at all"
|
||||
@@ -1599,14 +1615,15 @@ fn the_whole_slam_macro_line_picks_slam_early_and_heroic_strike_late() {
|
||||
// `VANILLA_SWING_MS` stand-in — and `st_timer` is still 0, so the whole swing reads as
|
||||
// elapsed:
|
||||
//
|
||||
// window = (2.0 - 1.5) / 2.0 = 25%, elapsed = 100% -> `[noslamclip]` false -> Heroic Strike.
|
||||
// maxSlamStart = 2.0 - 2.50 <= 0 -> window 0%, elapsed 100% -> `[noslamclip]` false ->
|
||||
// Heroic Strike.
|
||||
//
|
||||
// That is correct of the numbers it has and wrong about the weapon, and it heals on the first
|
||||
// auto-attack that lands (below). It is also why the latch is worth a log line
|
||||
// (`char_stats::with_unit_stats`): if the sheet never arrives, this *is* the session.
|
||||
assert_eq!(ask(&mut s, "st_timerMax"), "2");
|
||||
assert_eq!(ask(&mut s, "CleveRoids.GetSwingPercentElapsed()"), "100");
|
||||
assert_eq!(ask(&mut s, "CleveRoids.GetSlamWindowPercent()"), "25");
|
||||
assert_eq!(ask(&mut s, "CleveRoids.GetSlamWindowPercent()"), "0");
|
||||
assert_eq!(
|
||||
ask(&mut s, "CleveRoids.ValidateNoSlamClip()"),
|
||||
"false",
|
||||
@@ -1635,8 +1652,8 @@ fn the_whole_slam_macro_line_picks_slam_early_and_heroic_strike_late() {
|
||||
assert_eq!(ask(&mut s, "CleveRoids.GetSwingPercentElapsed()"), "0");
|
||||
assert_eq!(
|
||||
ask(&mut s, "CleveRoids.GetSlamWindowPercent()"),
|
||||
"44.444444444444",
|
||||
"(2.70 - 1.50) / 2.70, off the real weapon this time"
|
||||
WINDOW_PCT,
|
||||
"(2.70 - 2.50) / 2.70, off the real weapon this time — 0.2 s of a 2.7 s swing"
|
||||
);
|
||||
let _ = s.take_spell_casts();
|
||||
s.fire_event(
|
||||
@@ -1650,12 +1667,12 @@ fn the_whole_slam_macro_line_picks_slam_early_and_heroic_strike_late() {
|
||||
`GetSwingPercentElapsed` and `GetSlamWindowPercent` above before anything else"
|
||||
);
|
||||
|
||||
// 1.6 s later the swing is 59% elapsed, past the window: the second clause takes it.
|
||||
// 1.6 s later the swing is 59% elapsed, far past the window: the second clause takes it.
|
||||
s.tick(1.6);
|
||||
assert_eq!(
|
||||
ask(&mut s, "CleveRoids.ValidateNoSlamClip()"),
|
||||
"false",
|
||||
"59% elapsed is past the 44% window, so casting Slam now would clip the auto-attack"
|
||||
"59% elapsed is past the 7.4% window, so casting Slam now would clip the auto-attack"
|
||||
);
|
||||
let _ = s.take_spell_casts();
|
||||
s.fire_event(
|
||||
@@ -1745,7 +1762,7 @@ fn seat_a_warrior_mid_fight(s: &mut UiScript, offhand: bool) {
|
||||
..Default::default()
|
||||
}));
|
||||
for (id, name, cast) in [
|
||||
(1464u32, "Slam", Some("1.5 sec cast".to_string())),
|
||||
(1464u32, "Slam", Some("2.5 sec cast".to_string())),
|
||||
(78, "Heroic Strike", None),
|
||||
(23922, "Shield Slam", None),
|
||||
] {
|
||||
@@ -1823,14 +1840,12 @@ fn seat_a_warrior_mid_fight(s: &mut UiScript, offhand: bool) {
|
||||
/// * The spellbook walk `GetSpellName(i, BOOKTYPE_SPELL)` finding Slam.
|
||||
/// * `GameTooltip:SetSpell` on a **nil-parent scanning tooltip built from
|
||||
/// `GameTooltipTemplate`**, whose `<name>TextLeft<i>` the addon reads with `getglobal` looking
|
||||
/// for `"%d+%.?%d* sec cast"`. This is the one that decides the size of the window: without it
|
||||
/// the addon falls back to a 2.5 s constant, which against a 2.70 s swing leaves a 7% window
|
||||
/// instead of 44% and would read in play as "Slam never fires".
|
||||
/// for `"%d+%.?%d* sec cast"`. This is the one that decides the size of the window.
|
||||
/// * The `<OnUpdate>` delta reaching `SP_ST_OnUpdate(arg1)` as seconds.
|
||||
///
|
||||
/// **All five answer correctly** once the sheet is pushed, with a 44.4% window off a 1.5 s
|
||||
/// scanned cast time — and the asymmetry this test originally only *documented* turned out to be
|
||||
/// the whole defect. It is the first bullet's edge: before the stats feed has pushed,
|
||||
/// **All five answer correctly** once the sheet is pushed — see [`WINDOW_PCT`] for the window
|
||||
/// they produce and for the fixture mistake that made an earlier revision of this test read as a
|
||||
/// clean bill of health while the macro was still picking Heroic Strike every time. It is the first bullet's edge: before the stats feed has pushed,
|
||||
/// `UnitAttackSpeed("player")` answered `0`, and SP_SwingTimer's `ADDON_LOADED` arm latches that
|
||||
/// into `st_timerMax` behind `if not st_timerMax` — where `0` is truthy, so the guard never
|
||||
/// re-fires. The first write to that global in a session is therefore always the pre-push answer.
|
||||
@@ -1878,7 +1893,7 @@ fn the_slam_clip_conditional_answers_on_the_players_own_interface() {
|
||||
rank: Some("Rank 1".into()),
|
||||
cost: Some("15 Rage".into()),
|
||||
range: Some("Melee Range".into()),
|
||||
cast_time: Some("1.5 sec cast".into()),
|
||||
cast_time: Some("2.5 sec cast".into()),
|
||||
description: "Slams the opponent.".into(),
|
||||
..Default::default()
|
||||
},
|
||||
@@ -1934,10 +1949,11 @@ fn the_slam_clip_conditional_answers_on_the_players_own_interface() {
|
||||
// The scanning tooltip: a nil parent, the shipped template, and `getglobal` down the lines.
|
||||
assert_eq!(
|
||||
ask(&mut s, "CleveRoids.GetSlamCastTime()"),
|
||||
"1.5",
|
||||
"the cast time came off the scanning tooltip, not the addon's 2.5 s fallback — the \
|
||||
fallback leaves a 7% window against a 2.70 s swing and reads as `[noslamclip]` never \
|
||||
being true"
|
||||
"2.5",
|
||||
"the cast time came off the scanning tooltip. On this realm it happens to equal the \
|
||||
addon's own 2.5 s fallback, so a scan that silently failed would read identically — what \
|
||||
this pins is that the `sec cast` line is reachable at all through a nil-parent \
|
||||
`GameTooltipTemplate`. [`WINDOW_PCT`] carries where the 2.5 comes from"
|
||||
);
|
||||
// The latch, before any swing. This loader runs the addon walk before the `set_*` calls
|
||||
// above, exactly as `load_ingame_ui_on_world_entry` runs it before any feed system — so this
|
||||
@@ -1984,8 +2000,9 @@ fn the_slam_clip_conditional_answers_on_the_players_own_interface() {
|
||||
);
|
||||
assert_eq!(
|
||||
ask(&mut s, "CleveRoids.GetSlamWindowPercent()"),
|
||||
"44.444444444444",
|
||||
"(2.70 - 1.50) / 2.70 — the share of the swing in which Slam still beats the auto-attack"
|
||||
WINDOW_PCT,
|
||||
"(2.70 - 2.50) / 2.70 — the share of the swing in which Slam still beats the \
|
||||
auto-attack: 0.2 s of 2.7, which is the whole story of this macro on this weapon"
|
||||
);
|
||||
assert_eq!(
|
||||
ask(&mut s, "CleveRoids.ValidateNoSlamClip()"),
|
||||
|
||||
Reference in New Issue
Block a user