Skip to content

feat(timers): ability reuse timers — Mend outcomes, Lay on Hands/Harm Touch, and two dead disciplines - #60

Merged
prokopto-dev merged 4 commits into
masterfrom
ao/nparseplus-4/ability-cooldown-timers
Aug 10, 2026
Merged

feat(timers): ability reuse timers — Mend outcomes, Lay on Hands/Harm Touch, and two dead disciplines#60
prokopto-dev merged 4 commits into
masterfrom
ao/nparseplus-4/ability-cooldown-timers

Conversation

@prokopto-dev

Copy link
Copy Markdown
Owner

Reported as "there is no automated timer for mend". Mend turned out to be
implemented and wired already — it just misses half of its outcomes — so this
also audits the rest of the long-reuse abilities and disciplines.

1. Mend fires on all four outcomes (fix)

The EQTool port matches two of Mend's four outcomes. The 6-minute reuse burns
on all four:

Outcome Log line Before
Success You mend your wounds and heal some damage.
Critical You magically mend your wounds and heal considerable damage.
Failure You have failed to mend your wounds.
Worsen You have worsened your wounds!

Note for the reporter: the two original lines were verified to work end-to-end
before this change, so if a plain success still shows no timer, the actual log
line is worth capturing.

2. Lay on Hands and Harm Touch (feat)

The two longest reuses a P99 character owns (72 minutes each) had no timer
in EQTool or here. New core/handlers/ability_cooldowns.py.

These lines name the target, never the user, so attribution is a guess and
a wrong guess strands a 72-minute row. Two gates, both chosen to under-detect
rather than over-detect:

  • Class — only a paladin gets Lay on Hands, only a shadow knight Harm Touch.
  • Direction — you harm-touch mobs and lay hands on people, so Harm Touch
    needs an NPC-looking target and Lay on Hands a non-NPC one. That discards the
    two common impostors: NPC shadow knights harm-touching the tank on aggro, and
    NPC paladins laying hands on themselves at low health. The self-target form of
    Harm Touch (You writhe in the grip of agony.) is never yours.

is_npc alone is not enough in either direction — it misses unnamed mobs
(a froglok knight) and matches plenty of ordinary player names (Bob is in
the master list) — so looks_like_npc pairs it with EQ's article convention.

Known residual: another paladin/shadow knight in view using theirs on the
same kind of target. Nothing in the line distinguishes it. The row dismisses
like any other, and a later You can use the ability ... again in line corrects
the countdown through DisciplineCooldownHandler. Flagging this explicitly as
the main thing to push back on if you disagree with the tradeoff.

3. Two disciplines that could never match a log line (fix)

Swept every * Discipline against the classes that can train it. 46 of 48
class/discipline pairs already worked
across all seven discipline classes;
two were silently dead, each for a different reason:

  • Puretone (bard 60) — its cast_on_you carries the file's trailing ..
    artifact. YouFinishCastingParser strips a trailing .. off the log line,
    so a .. in the data can never be the lookup key either way. C# normalizes
    exactly one spell (Defensive Discipline); extended to disciplines.
  • Whirlwind (monk 53) — a later classless duplicate named Whirlwind
    claimed its message key first and the C# index guard then dropped the classed
    self-buff entirely. The trainable spell now seats at the head; the duplicate
    is demoted, not discarded.

Also corrects Harm Touch's recast to 72 min (the file says 30 s for both knight
abilities; C# only fixes Lay on Hands).

Deliberately scoped: ~80 spells carry the .. artifact, but 17 normalize
onto a message another spell already owns, which would move existing best-guess
matches. Kept to disciplines; the wider fix wants its own issue.

Verification

  • 2014 passed (+~45 new), ruff clean.
  • Drove the real app offscreen against a live log file — 10/10 scenarios,
    including the spell window rendering the countdowns and both impostor lines
    correctly ignored:
ok  Lay on Hands (pal)     window='… | You | Lay on Hands | 1:11:59'
ok  Harm Touch (SK)        window='… | You | Harm Touch | 1:12:00'
ok  Puretone Disc (bard)   window='… | Puretone Discipline Cooldown | 1:11:59'
ok  Whirlwind Disc (monk)  window='… | Whirlwind Discipline Cooldown | 1:00:00'
ok  NPC self-LoH ignored   rows=[]
ok  NPC HT on you ignored  rows=[]

Game data confirmed against the P99 wiki (Lay Hands,
Harm Touch, Mend).

Please merge with a merge commit, not squash, so the fix/feat commits
survive for semantic-release.

🤖 Generated with Claude Code

prokopto-dev and others added 3 commits August 9, 2026 22:20
The EQTool port matches only the plain success and plain failure lines, so a
monk who crit-mended or worsened their wounds got no reuse timer at all. Mend
has four outcomes and the 6-minute reuse burns on every one of them.

Adds the critical success ("You magically mend your wounds and heal
considerable damage.") and critical failure ("You have worsened your
wounds!") lines, and covers all four at both the parser and handler level.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… line

Swept every "* Discipline" in the book against the classes that can train it:
46 of 48 class/discipline pairs already produced a cooldown row, and two were
silently dead for different reasons.

Puretone (bard 60): its cast_on_you carries the file's trailing ".."
artifact. YouFinishCastingParser strips a trailing ".." off the LOG line, so
a ".." in the DATA can never be the lookup key either way. C# normalizes
exactly one spell (Defensive Discipline); extend that to disciplines. Kept to
disciplines on purpose — ~80 spells carry the artifact but 17 of those
normalize onto a message another spell already owns, which would move
existing best-guess matches.

Whirlwind (monk 53): a later classless duplicate named "Whirlwind" claimed
its message key first, and the C# index guard then dropped the classed
self-buff entirely. Seat the trainable spell at the head instead; the
duplicate stays as a lower-priority candidate.

Also corrects Harm Touch's recast to 72 minutes — the file claims 30 s for
both knight abilities and C# only fixes Lay on Hands.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The two longest reuses a P99 character owns (72 minutes each) had no timer in
EQTool or here, so forgetting one is expensive. Both are real rows in
spells_us.txt, so the cast messages and the reuse length come from the spell
data rather than literals.

Attribution is the hard part and it bounds what the handler may claim: these
lines name the TARGET, never the user. Two gates keep the guess honest, both
chosen to under-detect rather than invent a 72-minute row:

- Class: only a paladin gets Lay on Hands, only a shadow knight Harm Touch.
- Direction: you harm-touch mobs and lay hands on people, so Harm Touch needs
  an NPC-looking target and Lay on Hands a non-NPC one. That discards the two
  common impostors — NPC shadow knights harm-touching the tank on aggro, and
  NPC paladins laying hands on themselves at low health. The self-target form
  of Harm Touch is never yours for the same reason.

is_npc alone is not enough in either direction (it misses unnamed mobs and
matches plenty of ordinary player names), so looks_like_npc pairs it with
EQ's own article convention.

What survives: another paladin or shadow knight in view using theirs on the
same kind of target. The row dismisses like any other, and a later "You can
use the ability ... again in" line corrects the countdown.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@prokopto-dev prokopto-dev left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes requested. The Mend and discipline fixes look solid, and the full suite passes (2014 passed, 2 deselected). The new Harm Touch attribution has a false-positive path that contradicts its stated under-detection policy: common player names in the master NPC list are treated as NPC targets, so an NPC harm-touching such a player creates a false 72-minute timer for the active character.

Comment thread src/nparseplus/core/handlers/ability_cooldowns.py Outdated
Review catch: the Harm Touch branch used the same NPC predicate as Lay on
Hands, and that predicate trusts the master NPC list. The list is every NPC
name in the game and 1676 of its entries are single words, so it also matches
ordinary player names ("Bob", "Fang", "Raven"). An NPC shadow knight
harm-touching player Bob therefore started a false 72-minute row — the exact
failure this module claims to refuse, and the one direction where a wrong
"NPC" invents a timer instead of missing one.

The two directions need opposite biases, so there are now two predicates:

- certainly_npc (article convention only) gates Harm Touch, where a wrong
  "yes" invents a row. Cost: a named raid mob no longer starts one.
- possibly_npc (article OR master list) gates Lay on Hands, where a wrong
  "yes" only costs a missed row, so any suspicion declines.

Adds the collision regression across all three colliding names, asserting the
collision still exists so the test cannot rot into a tautology, plus a guard
that certainly_npc stays strictly weaker than possibly_npc.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@prokopto-dev
prokopto-dev merged commit 3abb5bc into master Aug 10, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant