# Monsterbation 1.4.1.2 — Battle Interaction Patterns Deep Dive ## Complementary Analysis to hv-scripts-analysis.md =========================================================================== ## 1. HOVER ATTACK SYSTEM — EXACT FLOW ### 1.1 The Complete Lifecycle (mouse enter → monster click) ``` MOUSE ENTER monster area ↓ Monsters() [line 1318] attaches event listeners: - mouseout → ClearTarget on .btm{Cfg.hoverArea} - mouseover → SetTarget(i) on .btm{Cfg.hoverArea} - mousedown → HandleClick(i) on full .btm1 - contextmenu → preventDefault on full .btm1 - wheel → HandleWheel(i) on full .btm1 ↓ SET TARGET(i) [line 1378] fires: target = i; // Set the global target variable THEN: if hover enabled, no interrupt, no alert, monster alive: → Hover(); ↓ HOVER() [line 1359]: if (hovering) return; // Guard: one hover per event cycle hovering = true; // Priority chain for action selection: if (override) → override(); // from mouseEngage/monsterBar else if (shiftHeld) → cfg.hoverShiftAction(); else if (ctrlHeld) → cfg.hoverCtrlAction(); else if (altHeld) → cfg.hoverAltAction(); else → cfg.hoverAction(); // Inject one-time impulse action after configured action: if (impulse) { impulse(); // Executes the impulse done = true; // Prevents re-trigger impulse = false; // Clears the impulse } monsters[target].click(); // CRITICAL: always clicks the monster // This is the actual turn submission! ↓ SERVER processes turn → new HTML page loads → loop repeats ``` ### 1.2 Key Design Decisions **Monster click ALWAYS fires last.** This is the single most important architectural pattern: the `monsters[target].click()` on line 1376 is the final action in Hover(). Every spell cast, item use, or toggle happens *before* it. The monster click triggers the page's built-in onclick handler, which submits the turn to the server. **Hovering flag prevents re-entry.** The `hovering` boolean (line 1360) prevents Hover() from being called recursively. It's reset to `false` in Observe() (line 1130) after each MutationObserver-triggered turn cycle. **hoverArea config (line 135-136, 870-871):** Controls which sub-element of `.btm1` triggers the mouseover: - 1: whole monster box - 2: monster icon - 3: monster name - 4: monster vitals/HP bar - 6: monster status effects area ### 1.3 Interrupt System Two global booleans control whether hover fires: | Flag | Set By | Meaning | |------|--------|---------| | `interruptHover` | `ToggleHover()` or `cfg.startRoundWithHover` | User manually toggled hover on/off | | `interruptAlert` | `Alerts()` + `Durations()` | Spark, low HP/MP/SP, or buffs expiring | InterruptAlert is set per-turn in: - `Alerts()` [line 1196-1245]: Checks spark (fallenshield.png without bar_dgreen.png), low HP, low MP, low SP - `Durations()` [line 1247-1293]: Checks alertBuffs regex against effect icons with < 2 turns remaining The `minSP` auto formula [line 1002]: `0.5 - 0.5 * spboost / (spboost + 100)` — dynamically scales based on Spirit Tank upgrades. ### 1.4 Modifier Key Hover Overrides When a modifier key is held during hover, the action changes: ``` shiftHeld + cfg.hoverShiftAction → evaluated during Hover() via handleKeys() ctrlHeld + cfg.hoverCtrlAction → syncs on keydown/keyup altHeld + cfg.hoverAltAction → same mechanism ``` The modifier state is tracked via `handleKeys()` [line 670] and `handleKeyup()` [line 686] which update `shiftHeld`, `ctrlHeld`, `altHeld` on every key event. This is separate from the modifier-based keybinding system. ### 1.5 Mouse Engage Mode When `cfg.mouseEngage = true` [line 989-991]: - mousedown sets `override` based on which mouse button: left→`cfg.clickLeft`, middle→`cfg.clickMiddle`, right→`cfg.clickRight` - mouseup clears override and sets `release = true` - Hover() then calls `override()` instead of `cfg.hoverAction` ### 1.6 Hover Autoresume On keyup [line 686-695]: If `cfg.hoverAutoresume` is true, clears `interruptHover` and re-fires Hover() if conditions permit. This enables the "hold key to modify, release to resume" workflow. =========================================================================== ## 2. SPELL ROTATION SYSTEM ### 2.1 Strongest() — The Core Combinator ```javascript function Strongest(actions) { return function() { var n = actions.length; while (n-- > 0) actions[n](); // Executes from LAST to FIRST }; } ``` **Critical detail:** `Strongest` iterates backwards (from last to first). This means: - FOR TARGETED SPELLS: put most desired action **first** in array — it gets called last (closest to monster click) - FOR UNTARGETED SPELLS/ITEMS: put most desired **last** in array — it gets called first (before any targeting) Why? Because targeted spells set up state (via the dummy element trick) that the monster click then resolves. The LAST spell run in the loop is the one whose state is active when `monsters[target].click()` fires. Untargeted actions complete immediately and don't need the monster click. ### 2.2 Impulse() — One-Shot Injection ```javascript function Impulse(action) { return function() { if (done) return; // Only fires once per turn cycle impulse = action; // Stores for later execution in Hover() if (interruptHover || interruptAlert || !monsters[target] || !monsters[target].hasAttribute('onclick')) { action(); // Immediate execution if hover is inactive done = true; impulse = false; } }; } ``` The Impulse pattern: If hover is active and healthy, the action is stored in the `impulse` variable and waits for the next Hover() call. If hover is stopped or no target, it fires immediately. The `done` flag is reset when `release` is set (on mouseup/keyup), allowing one impulse per user interaction. ### 2.3 How Spell Icons Are Found in the DOM ```javascript function Cast(name) { return function() { var spell; // Guard: don't recast the currently active spell if (document.getElementsByClassName('btii')[0].innerHTML != name && // Find spell icon by its onmouseover text containing the spell name (spell = document.querySelector('.bts > div[onclick][onmouseover*="\\\'' + name + '\\\'"]'))) { // DUMMY ELEMENT TRICK: dummy.setAttribute('onclick', spell.getAttribute('onmouseover')); dummy.click(); // Triggers the spell's onmouseover → sets targeting mode spell.click(); // Clicks the actual spell icon → selects it } }; } ``` **Selector breakdown:** `.bts > div[onclick][onmouseover*="'SpellName'"]` - `.bts` = battle spell containers - `div[onclick]` = only clickable divs (available spells) - `[onmouseover*="'SpellName'"]` = substring match on the onmouseover attribute The onmouseover attribute on spell icons contains text like: `('Imperil', 3)` — indicating the spell name and turn cost. The regex index `\''` is the game's way of representing the spell name in the attribute. **Guard against recasting:** Line 336 checks `document.getElementsByClassName('btii')[0].innerHTML != name` — this is the "currently selected spell" indicator at the top of the battle page. If Imperil is already queued, it won't try to cast it again, preventing wasteful clicks. ### 2.4 The Dummy Element Trick The `dummy` element (line 735) is a detached `