hv-unified/references/jpx-analysis.md
GaboGG 402db6bb2f Initial commit: HV Unified v0.11.0 structure
- 23 source files in src/ (build via scripts/build.sh)
- Forum-sourced player knowledge in references/
- DESIGN.md with architecture and corrections
- References to existing scripts (Monsterbation, jpx, HV Utils)
2026-07-20 19:30:26 -04:00

34 KiB
Raw Blame History

Deep-Structural Analysis of jpx HentaiVerse Auto-Battler Userscript

File: /home/gabogg/Downloads/jpx20260706.txt Version: 2026.07.06 Size: 270,752 bytes, 6,215 lines Language: Vanilla JavaScript (no framework), single-file userscript


1. ARCHITECTURE

1.1 Initialization Flow

The entire script is a single-file userscript with a declarative bootstrap:

  1. Userscript Header (lines 1-11): @run-at document-end, matches *.hentaiverse.org/*, excludes /equip/* and /isekai/equip/*. No GM_ grants needed — works purely via DOM.

  2. Module Bootstrap (lines 463-465): Three singleton-style modules are immediately invoked:

    • jpxPanelManager() — creates/configures the control widget overlay
    • jpxMarket() — market price fetcher/cacher
    • jpxUtils() — utility library

    Each follows the pattern function jpxThing() { const ns = jpxThing; if (ns._init) return ns; ... ns._init = true; return ns; } — single-init guard via _init flag on the function object itself.

  3. Entry Point (line 6215): initDo() is called at file's end.

  4. initDo() (line 1242): The router function that:

    • Injects CSS via <style id="jpx">
    • Registers beforeunload handler (storeTmp) for crash-safe state persistence
    • Registers pointerdown handler to dismiss multi-select popup panels
    • Registers keydown handler with throttled actionManager (75ms throttle)
    • Initializes I18N (initDoI18n)
    • Page Detection via document.querySelector('#textlog'):
      • If #textlog exists AND doInitDoBattle is falseinitDoBattle() — we're in a battle
      • If #riddlemaster existsriddleRecorder() — RiddleMaster encounter
      • If no #textlog (Lobby) → reads player info (level, stamina, difficulty, persona, spell damage bonus) from DOM selectors and stores in localStorage

1.2 Hooking Into the Game

The script hooks into the game at three levels:

  1. MutationObserver on #textlog (line 1541): A MutationObserver watches log.firstChild for childList changes. Every time new battle log text is added, preProcessLog() fires (throttled at 200ms, trailing). This is the primary event loop.

  2. DOM event listeners:

    • keydown on document (capture phase, line 1255) — for hotkeys and key-bound battle actions
    • pointerdown on document (line 1249) — for dismissing multi-select panels
    • mousemove on window (line 1366) — sets jpxPanelManager.ready = true
    • DOMContentLoaded for reDoBattle() on round transitions (line 1369)
    • beforeunload on window (line 1248) — calls storeTmp() to persist state
  3. Custom AJAX round advancement (lines 1623-1716): When ajaxRound is enabled, the script intercepts the "Continue" button (#btcp) onclick, replaces it with an async XHR-based flow that fetches the next round HTML, handles RiddleMaster encounters inline, and re-initializes the game's window.Battle object via document.dispatchEvent(new Event('DOMContentLoaded')).

1.3 Page/Context Detection

The script detects its environment via DOM selectors with no URL parsing beyond the isekaiSuffix (line 475):

Condition State Action
#textlog exists + !doInitDoBattle In battle initDoBattle()
#riddlemaster exists RiddleMaster riddleRecorder()
No #textlog, Bazaar AM modify or Battle IW Lobby (IW equipment screen) Observe #equipform for world level/difficulty
No #textlog, s=Battle query Lobby (battle selection) Read level, stamina, persona, spell damage bonus
Default Lobby (other) No action

2. RULE ENGINE

2.1 Configuration Structure

Battle configuration is stored in cfgBattle (line 13), merged from:

  • defaultCfgBattle (lines 14-229) — hardcoded defaults for all fighting styles
  • localStorage['jpx_cfgBattle' + isekaiSuffix] — user overrides
  • Merged by mergeCfg() (line 5034) with version-aware reset logic

The config schema for each battle mode ({Style}_{Type}) has three sections:

  • supports — array of support actions (healing, buffs, items) evaluated first
  • attacks — array of offensive actions evaluated second
  • kb_* (key-bindings) — direct hotkey-triggered action sequences stored as dynamic keys

The supported battle mode keys follow BATTLE_MODES (line 657):

Style × Variant: 10 styles × 6 types = 60 possible modes
Styles: OneHanded, 1H_Mage, TwoHanded, 2H_Mage, DualWielding, DW_Mage, NitenIchiryu, NI_Mage, Staff, Unarmed
Types:  General, Arena, Encounter, Colosseum, Battle1000, Item, Tower

2.2 Action Types

Each action is an object with a type discriminator and optional conditions array:

Type Description Parameters
stop Halts auto-battle with custom message customMessage, conditions
spellSupport Casts a supportive spell name (from SPELLS_SUPPORT), conditions
item Uses an inventory item name (from ITEMS), conditions
toggle Toggles Spirit Stance/Defend/Focus name, toggled (boolean), conditions
target Selects a target monster for subsequent actions priorityRule (from PRIORITY_RULES), conditions
smartDebuff Casts AoE debuffs with spatial targeting name, targetCount (1-3), bottomUp, tailSkip, maxAtFirst, minMonstersLeft, conditions
spellDebuff Casts single-target debuff on selected monster name, conditions
spellDamage Casts offensive spell on selected monster name (can be T1/T2/T3 aliases), conditions
skill Uses a weapon skill name (from SKILLS), conditions
normalAttack Basic attack on selected monster None

2.3 Condition System

Condition Categories

General Conditions (conditionsGeneral, lines 947-979): Checked against global/player state:

  • world — Persistent vs Isekai
  • pLevel — Player level range
  • pMaxSpellType — Player's strongest spell element
  • battleTypes — Battle type(s) (Arena, Encounter, etc.)
  • difficulty — Difficulty array
  • roundCurrent, roundLeft, roundTotal — Round position
  • floor — Tower floor range
  • pActionCooldown — Cooldown range of specific actions (skill/spell/item)
  • pActionCounts — Uses-per-round range of specific actions
  • pHP, pMP, pSP — Player resource percentages
  • pOC — Overcharge percentage
  • pSpiritStatus — Spirit Stance active?
  • pEffects — Player has effects with turns in range
  • pIgnoredEffects — Player does NOT have these effects
  • pEffectStacks — Player effect stack counts
  • monsters, activeMonsters, defeatedMonsters — Monster counts
  • bosses, activeBosses, defeatedBosses — Boss counts
  • mLevel — Monster level range
  • mWithoutEffects — Count of monsters without specific effects

Target Conditions (conditionsTarget, lines 980-999): Checked per-monster with offset/matched mechanics:

  • tName — Regex-matchable monster name
  • tTypes — Monster type (Normal, Rare, Legendary, etc.)
  • tClasses — Monster class from MonsterDB (Arthropod, Avion, etc.)
  • tPowerLevel — Monster power level range
  • tIndex — Monster position (0-based, negative from end)
  • tHP, tMP, tSP — Monster resource percentages
  • tEffects — Monster has specific effects
  • tIgnoredEffects — Monster does NOT have specific effects
  • tEffectStacks — Monster effect stacks
  • tDaysSinceUpdate — Days since MonsterDB last updated entry

Each target condition carries offset (range relative to target's index) and matched (count of monsters in the offset range that must satisfy the condition — defaults to [1,1] = exactly 1).

Condition Evaluation (checkConditions, line 2579)

function checkConditions(conditions, target, checkGlobal, checkTarget)
  • Iterates through all conditions, short-circuiting on first failure (AND logic)
  • Global conditions: checked directly against global state objects
  • Target conditions: evaluated with offset-based multi-monster checking
    • successCount increments for each active monster in [target.index + offset[0], target.index + offset[1]] that satisfies the condition
    • Final check: jpxUtils.inRange(successCount, matched)

The conditions system is lazily initialized via initConditions() (line 2445) which creates handler maps. Results are cached in conditionsObj per evaluation cycle (cleared in preRender()).

2.4 Action Execution (actionManager, line 2734)

The action manager processes action sequences with a sophisticated targeting phase:

actionManager(actions):
  targetPhase = false
  targetMonster = null
  targetLocked = false

  for each action in actions:
    if action.disabled → skip
    
    if action.type == 'target':
      targetPhase = true
      if not targetLocked:
        result = handler(action, null)  // evaluates target conditions globally, returns matching monster or null
        if result found:
          targetMonster = result
          targetLocked = true  // Lock in the first matching target
      
    else if targetPhase and no targetMonster → skip (no target to act on)
    
    else if handler(action, targetMonster) succeeds → return true (action taken)
      targetLocked = false  // Unlock for next target rule
    
  return false  // No action was possible

Key behaviors:

  • Target rules cascade: Once a target rule finds a monster, targetLocked prevents re-evaluation until an action is successfully executed
  • Smart Debuff skips target phase: smartDebuff actions are checked globally (conditions must pass without target), then internally handle their own targeting
  • Non-target actions after target rules require a locked target, but "stop"/"item"/"spellSupport"/"toggle" can execute without a target if they appear before any target rule

2.5 Smart Debuff System (doSpellsDebuffGoNext, line 1810)

This is the most advanced targeting system in the script. It handles multi-target AoE debuff placement:

Parameters:

  • name — Spell name
  • targetCount — 1, 2, or 3 monsters to hit
  • bottomUp — Iterate from monster J→A (true) or A→J (false)
  • tailSkip — Skip last N monsters (positive) or only consider first N (negative)
  • maxAtFirst — Maximum casts when ≤1 monster defeated
  • minMonstersLeft — Threshold for infinite casting when many monsters remain

Algorithm:

  1. Check cooldown and action count limits
  2. Calculate startIndex/endIndex range from activeMonsters bounds
  3. tailSkip: if positive, trim from end; if negative, limit to first N from start
  4. First pass (targetCount ≥ 3): Find 3 consecutive alive + undebuffed monsters → cast on middle
  5. Second pass (targetCount ≥ 2): Find 2 consecutive alive + undebuffed → cast on edge-appropriate monster
  6. Third pass (targetCount ≥ 3, relaxed): Find 3 consecutive where middle is alive → cast on middle
  7. Fourth pass (targetCount = 1): Any single undebuffed monster → cast

isUnDebuffed memoization (line 1843): Results cached in undebuffedObj to avoid re-evaluating conditions for the same index.

2.6 Priority Rules for Target Selection

Six rules (line 658):

  • Top Down — Default, monster A first
  • Bottom Up — Monster J first
  • Current HP Low to High — Sort by absolute HP
  • Current HP High to Low — Sort by absolute HP descending
  • Current HP Percent Low to High — Sort by HP percentage
  • Current HP Percent High to Low — Sort by HP percentage descending

Results are cached in monstersObj.sorted per priority rule per cycle.


3. DOM PARSING

3.1 Monster State (getMonsters, line 2191)

Parses all .btm1 elements to build monster objects:

monster = {
  index,                    // 0-9
  click(),                  // clicks the monster DOM element
  level,                    // from .btm2 div text or HVClasses
  name,                     // from .btm3 div text or HVClasses
  type,                     // matched against bossTypes map (Normal/Rare/Legendary/etc.)
  hpPercentage,             // from .btm4 green bar width / 120
  mpPercentage,             // from .btm4 blue bar width / 120
  spPercentage,             // from .btm4 red bar width / 120
  isAlive,                  // .btm1 has onclick attribute
  effectObj: { name: { turns, stack } },  // from .btm6 img tooltips
  monster_btm1              // reference to DOM element
}

Returns: { monsters, activeMonsters, bosses, activeBosses }

3.2 Player Vitals (getVitals, line 2091)

Two code paths:

  • With OC bar (Arena/Tower): widthHP=414, widthMP/SP/OC=414, reads #dvrhb/#dvrhd, #dvrm, #dvrs
  • Without OC bar (Encounter/Grindfest): widthHP=496, widthMP/SP=207, reads #vrhb/#vrhd, #vrm, #vrs

OC calculation:

  • With bar: 10 * parseInt(ocBar.style.width) / 414
  • Without bar: Count <div> elements in #vcp HTML (each div = 0.5 OC after the first), handle half-segment via vcr class

Returns: { oc, hpPercentage, mpPercentage, spPercentage, hpCurrent, mpCurrent, spCurrent, hpMax, mpMax, spMax }

3.3 Action Cooldowns (getActionCooldowns, line 1950)

Dual scanner:

  • Spell cooldowns (.bts > div[onmouseover]): Parse onmouseover tooltip with regExp.spellInfo → extract name and cooldown. If element has onclick, cooldown = 0 (ready). Otherwise: lastUse + initCooldown - currentTurn
  • Item cooldowns (.bti3 > div): Parse with regExp.itemInfo → map item ID to name via itemMap. Fixed 40-turn cooldown.

Cooldown is '-' if still on cooldown, 0 if ready.

3.4 Player Effects (getEffectDuration, line 2145)

Parses #pane_effects images and .btm6 > img elements:

  • Extract from onmouseover tooltip using regExp.spellMatch with named groups: name, stack, description, turns
  • Builds playerEffectsObj[name] = { turns, stack }
  • When render=true: creates duration overlay divs with color coding (<4 turns aquamarine, <9 lavender, auto/permanent special display)

3.5 Monster Effects Tracking (updateMonsterEffects, line 2291)

Handles the game's limitation of only showing 5 status effects per monster:

  • 5 effects visible: Reset saved state completely
  • 6 effects visible: The 6th is hidden behind a scroll — the script:
    1. Calculates hidden turn deltas by comparing saved vs visible effect turns
    2. Parses battle log for effect gains/expirations (regExp.effectGain, regExp.effectExpired, regExp.effectWear, etc.)
    3. Applies turn decrement to hidden effects
    4. Special handling for elemental effects (Searing Skin, Freezing Limbs, etc.) and Coalesced Mana
    5. Prunes effects that would have expired
    6. Renders hidden effect icons into the .btm6 DOM

3.6 Monster Info (MonsterDB Integration, line 2404)

Polls window.HVMonsterDB.getCurrentMonstersInformation() with 250ms timeout for:

  • monsterClass — e.g., "Arthropod", "Daimon"
  • attack — attack type
  • plvl — power level
  • lastUpdate — timestamp for tDaysSinceUpdate condition

Stored in allMonsterInfo keyed by mkey_{0-9}.

3.7 Spell/Item Casting (cast / use, lines 1928-1948)

cast(name): Searches .bts > div[onclick][onmouseover*="'name'"] for spell buttons. If current selected spell differs, fires onmouseover (to select) then onclick (to cast) via a dummy div.

use(name): Looks up item ID from itemMap, finds matching div in .bti3 > div[onclick][onmouseover] containing that ID, fires onmouseover + onclick.

3.8 Spirit Status (getSpiritStatus, line 2139)

Checks if #ckey_spirit outerHTML contains spirit_a.png (active) or not (inactive).


4. FIGHTING STYLES

4.1 Auto-Detection (initDoBattle, line 1364)

Detection is based on which skills appear in the spell quickbar:

Available Skill Style (low spell damage) Style (high spell damage) Threshold
Shield Bash OneHanded 1H_Mage 70
Great Cleave TwoHanded 2H_Mage 100
Iris Strike DualWielding DW_Mage 100
Skyward Sword NitenIchiryu NI_Mage 100
Concussive Strike Staff Staff N/A
None of above Unarmed Unarmed N/A

The threshold compares spellDamageBonus.maxValue (from localStorage['jpx_spellDamageBonus']) — the player's maximum spell damage bonus value. Above threshold = mage variant.

4.2 Battle Mode Resolution (getBattleMode, line 4173)

getBattleMode(defaultBattleStyle = 'Unarmed'):
  modeKey = `${battleStyle}_${battleType}`  // e.g., "OneHanded_Arena"
  
  if cfgBattle[modeKey] has non-empty supports, attacks, or key-bindings:
    return modeKey  // Specific mode exists
  
  return `${battleStyle}_General`  // Fall back to general mode

This means: check for a battle-type-specific config first, fall back to general.

4.3 Battle Type Detection (initDoBattle, lines 1410-1428)

Parses #textlog innerHTML with regExp.battleTypeLog:

  • arena challenge + NOT Round 1 / 1Arena
  • random encounterEncounter
  • arena challenge + Round 1 / 1Colosseum
  • GrindfestBattle1000
  • Item WorldItem
  • The TowerTower (also extracts floor number)

4.4 Style-Specific Configurations (Default)

The bundled defaults show the script author's own configurations:

  • OneHanded_General: Full support chain (Spirit→Health→Mana gems/potions/elixirs, Mystic Gem, Heartseeker, Regen, Draughts), attacks use smartDebuff (Weaken for Arena400/500), target rules by priority, Scan first round, skills (OFC, Merciful Blow, Vital Strike), ending with normalAttack
  • OneHanded_Tower: Complex multi-stage smart debuff chain (Sleep→Weaken→Silence→Imperil) triggered by floor/round thresholds
  • Staff_General: Mage-focused supports (Arcane Focus instead of Heartseeker), smartDebuff chain (Weaken→Silence→Imperil), target priority for Coalesced Mana monsters, T3→T2→T1 spell priority
  • Other styles (2H, DW, Niten, Unarmed, mage variants): Empty default configs (lines 126-173, 221-228) — users configure them

5. BATTLE MODES

5.1 Enumeration (LINE 656-657)

BATTLE_TYPES = ['Arena', 'Encounter', 'Colosseum', 'Battle1000', 'Item', 'Tower']
BATTLE_STYLES = ['OneHanded', '1H_Mage', 'TwoHanded', '2H_Mage', 'DualWielding', 'DW_Mage', 'NitenIchiryu', 'NI_Mage', 'Staff', 'Unarmed']
BATTLE_MODES = Cartesian product BATTLE_STYLES × ['General', ...BATTLE_TYPES]

5.2 Mode-Specific Behavior

Battle Type Special Behavior
Arena Sets difficulty to PFUDOR if ≥90 rounds. Uses roundInfo from localStorage for persistence
Encounter Round info not tracked. Stamina cost = 0
Colosseum Single round. Stamina cost = 0
Battle1000 (Grindfest) Stamina cost includes +1 entry fee
Item (Item World) Reads worldLevel from localStorage (populated in lobby from IW equipment screen). Stamina cost formula with great/normal rates
Tower Auto-derives difficulty from floor number (1-6 Normal, 7-13 Hard, ..., 34-39 IWBTH, 40+ PFUDOR). Caches floor in localStorage

5.3 Difficulty Map (line 574)

{ Normal: 1, Hard: 2, Nightmare: 4, Hell: 7, Nintendo: 10, IWBTH: 15, PFUDOR: 20 }

5.4 Stamina Cost Calculation (getStaminaCost, line 3172)

  • Great cost: 0.03 (Persistent) or 0.06 (Isekai) per round
  • Normal cost: 0.02 (Persistent) or 0.04 (Isekai) per round
  • Great rounds capped by floor(max(0, stamina - 60) / greatCost) (60 stamina reserved for normal-cost rounds)
  • Battle1000 adds +1 entry fee
  • Daily quota tracking via staminaRecords in localStorage per date

6. KEYBINDINGS & UI

6.1 Hotkey System

Global Keybinds (line 912):

KEYBINDS = {
  openBattleRecords: { key: 'z', ctrl: false },
  toggleActive:      { key: 'm', ctrl: false },
  openSettings:      { key: ',', ctrl: false }
}

User-customizable, stored in localStorage['jpx_userKeybinds' + isekaiSuffix].

Battle Action Keybindings: Stored as kb_Ctrl+Shift+A style keys in each battle mode config. Processed in onKeyDown():

  1. Check for global keybind match (non-repeat only)
  2. If inactive and #textlog exists and monsters are alive, check for mode-specific keybinding
  3. Repeat keys use throttled actionManager (75ms), initial press uses direct actionManager

Key Capture (jpxUtils.captureKeyCombo, line 6060): Uses AbortController for clean cancellation. Ignores modifier-only keypresses. Fires on first non-modifier keydown or pointerdown.

6.2 Control Widget (ctrlWidget)

Created by jpxPanelManager.createCtrlWidget('battle'):

  • Positioned absolutely at top-right of battle screen (responsive via media query for landscape)
  • Shows configurable rows (ctrlWidgetRows): Active status, Ready state, Network delay, Battle style, Battle type, Battle mode, Round
  • Background color signals state: green (#4f4) = active, pink (#fef) = inactive, yellow (#ff5) = warning
  • Click toggles auto-battle
  • Sub-buttons: "Open Stats" → opens battle records in new window; "Open Settings" → opens settings panel
  • Optional mouseEnter trigger for auto-battle activation
  • Dispatches jpx_ctrlWidget_update CustomEvent for inter-script communication

6.3 Settings Panel (press ,)

renderSettings() creates a full in-page settings panel with:

  • Two tabs: Battle Settings, Stats Settings
  • Battle tab: Keybind remapping UI, full schema-driven config editor for all battle modes, export/import/reset current mode
  • Stats tab: Dark mode toggle, combat/revenue row picker, stats column picker, IndexedDB export/import
  • Schema-driven rendering: renderSchema() with fieldRenderers registry supporting: heading, constant, boolean, text, number, rangeNumber, dropdown, array (with drag-and-drop reordering), fieldPicker (dual-list), object, keyBasedObjectArray (for keybindings), conditionsArray

6.4 Toast Notifications

jpxUtils.createToast(content, duration) — fixed-position toast at bottom-right with fade animation.


7. STORAGE

7.1 localStorage (prefix: jpx_, suffixed with _isekai for Isekai)

Key Data Purpose
cfgBattle Full battle config User rules, passed through mergeCfg
cfgStats Stats display config Combat/revenue display preferences
userKeybinds Custom keybindings Overrides for global hotkeys
spellDamageBonus {maxType, maxValue} Spell element optimization
worldLevel Number IW world level
difficulty String Current difficulty
playerLevel Number Player level
persona String Active persona
stamina Number Current stamina
battleType String Last battle type
towerFloor Number Current tower floor
roundInfo {current, total} Round tracking across page loads
monsterData Array of {id, name, level, maxHP} Monster data for HP calculation
battleLogRecord Array of strings Raw battle log (if enabled)
timeRecords {action, turn, riddle, lastUse} Per-battle timing
combatRecords Nested damage/result stats Per-battle combat data
revenueRecords Nested drop/currency stats Per-battle revenue tracking
staminaRecords {lastUpdate, staminaCost} Daily stamina quota tracking
priceData Market prices Cached once per day

7.2 IndexedDB

Database: jpx, version 1 Object Store: battleRecords with keyPath timestamp, index on date

Used for persistent battle history. Records contain: world, timestamp, date, playerLevel, difficulty, persona, battleType, worldLevel, towerFloor, roundInfo, result, deltaSeconds, deltaTime, turns, tps, riddle, combatRecords, revenueRecords.

Operations:

  • openDB() — Open/create database
  • storeBattleRecords() — Save after battle completion
  • getBattleRecordsRender() — Query with filters (aggregate by day, world, battleType, difficulty, result, round range)
  • exportIndexedDB() / importIndexedDB() — Full DB export/import with optional merge

7.3 State Management

  • storeTmp() (beforeunload): Persists monsterData, battleLogRecord, timeRecords, combatRecords, revenueRecords — but only if battle is not complete (no finish button)
  • On battle completion: localStorage.removeItem() clears all temp records
  • Version-aware config migration via mergeCfg() comparing battleVersion/statsVersion

8. COMPATIBILITY

8.1 Monsterbation (Monster DB Script) Integration

The script integrates with window.HVMonsterDB (Monsterbation's monster database):

  • monsterDBReady() (line 2404): Polls window.HVMonsterDB.getCurrentMonstersInformation() with 250ms timeout
  • Used for: monsterClass, attack type, plvl (power level), lastUpdate date
  • tClasses, tPowerLevel, tDaysSinceUpdate conditions depend on this data
  • showMonsterInfo option displays monster class, attack type, and power level on each monster
  • Falls back gracefully ('?' display) when MonsterDB is unavailable

8.2 Inter-Script Communication

jpx_ctrlWidget_update CustomEvent is dispatched by jpxPanelManager.dispatchState() (line 5614) with:

{ active: isActiveBattle, background: color, suffix: isekaiSuffix, timestamp }

This is the designated hook for other userscripts to react to jpx state changes.

8.3 AJAX Round Compatibility

The ajaxRound feature has a config flag with explicit warning: "Disable it if other scripts don't support it." When enabled, it replaces standard page navigation with XHR fetches, which may break other scripts that rely on full page loads.

8.4 DOM Content Loaded Simulation

On AJAX round advancement, the script fires document.dispatchEvent(new Event('DOMContentLoaded')) (line 1680) to retrigger other scripts' initialization. It also re-creates window.battle = new window.Battle() and calls clearInterval(window.timer).


9. KEY FUNCTIONS

Initialization & Lifecycle

Function Line Purpose
initDo() 1242 Router: detects page context and dispatches to battle/lobby/riddle flows
initDoBattle() 1364 Battle initialization: load configs, detect style/type, parse initial state
reDoBattle() 1457 Re-initialization on round transition / DOMContentLoaded
preDoBattle() 1476 Parse round info, proficiency, monster data; setup MutationObserver
preProcessLog() 1547 MutationObserver callback: processes new log entries
preRender() 1580 Prepares all state objects (cooldowns, vitals, monsters, effects) for decision
goNext() 1614 Main loop: checks if active, handles end-of-round, triggers smartBattle
storeTmp() 5469 beforeunload handler: persists temp state

Battle Logic

Function Line Purpose
smartBattle() 1792 Evaluates supports then attacks; returns whether action was taken
actionManager() 2734 Processes action sequences with target locking
checkConditions() 2579 Universal condition evaluator
initConditions() 2445 Builds general/target handler maps
doSpellsDebuffGoNext() 1810 Smart debuff spatial targeting algorithm
doSpellGoNext() 1897 Cast spell on specific monster
doAttackGoNext() 1907 Normal attack on specific monster
doToggleGoNext() 1916 Toggle Spirit/Defend/Focus
cast() 1928 Cast named spell via DOM
use() 1937 Use named item via DOM

DOM Parsing

Function Line Purpose
getActionCooldowns() 1950 Parse spell/item cooldowns from quickbar DOM; render quickbar extensions
getVitals() 2091 Parse HP/MP/SP/OC from bar elements
getSpiritStatus() 2139 Check if Spirit Stance is active
getEffectDuration() 2145 Parse player/monster effect durations from tooltips; render overlays
getMonsters() 2191 Build monster state objects from .btm1 elements
updateMonsterEffects() 2291 Track hidden (6th+) monster status effects
updateMonsterInfo() 2428 Poll MonsterDB for monster class/power/attack data
monsterDBReady() 2404 Async poller for MonsterDB availability

Battle Recording

Function Line Purpose
battleRecorder() 2767 Orchestrates all recording subsystems
battleLogRecorder() 2789 Records raw battle log text
timeRecorder() 2823 Tracks actions, turns, last use timestamps
riddleRecorder() 2830 Counts RiddleMaster solves
combatRecorder() 2839 Comprehensive damage/result tracking with type classification
revenueRecorder() 3012 Tracks EXP, credits, drops by category and quality

Battle Results Display

Function Line Purpose
battleRecordPlayer() 3127 Orchestrates end-of-battle record display and DB storage
battleLogPlayer() 3290 Creates downloadable battle log blob
timeRecordPlayer() 3301 Creates time/riddle/spark summary div
combatRecordPlayer() 3313 Creates combat stats table (damage, results, crit stacks, debuff resists)
combatRecordPlayer_Use() 3438 Creates action usage summary table
revenueRecordPlayer() 3478 Creates revenue table with drop/use/balance/profit
newWindowRecordPlayer() 3605 Renders mid-battle stats in popup window

IndexedDB & Stats

Function Line Purpose
openDB() 3672 Open/create IndexedDB
storeBattleRecords() 3695 Save battle record
openBattleRecords() 3711 Open stats window with filters
getBattleRecordsRender() 3799 Query/filter/aggregate battle records
exportIndexedDB() 3851 Full DB export
importIndexedDB() 3873 DB import with merge support
filterData() 3908 Apply filters to records
generateAggregate() 3918 Aggregate data by day (Total/Average)
createFilter() 4028 Build filter UI
getFilters() 4080 Read filter values from UI
renderDynamicTable() 4107 Render stats table with color thresholds

Settings & Schema

Function Line Purpose
renderSettings() 5094 Open/close settings panel
switchTab() 5265 Switch between battle/stats tabs
renderBattleTab() 5288 Battle settings tab with keybinds + full config editor
renderStatsTab() 5381 Stats settings tab with DB export/import
renderSchema() 4189 Schema-driven form renderer
renderField() 4989 Field dispatcher to type-specific renderers
resolveSchema() 4994 Resolve discriminator-based schemas
createEmptyObject() 5000 Generate default object from schema
getUniqueId() 5030 Generate unique DOM IDs
mergeCfg() 5034 Merge stored config with defaults, run patches/migrations

Configuration

Function Line Purpose
getBattleMode() 4173 Resolve active battle mode key (e.g., "OneHanded_Arena" → "OneHanded_General")

Keybinding

Function Line Purpose
onKeyDown() 1746 Global keydown handler
toggleActive() 1782 Toggle auto-battle on/off

I18N

Function Line Purpose
initDoI18n() 5491 Merge built-in I18N with external jpxI18N override
t() 5505 Translation function with dot-path lookup and templating

Modules (singleton pattern)

Module Line Purpose
jpxPanelManager() 5526 Control widget creation, content update, state dispatch
jpxMarket() 5632 Market price fetching, caching, default prices
jpxUtils() 5797 25 utility functions: throttle, time formatting, sorting, type checks, DOM helpers, XHR, key capture, HV class parsing, record factories

Utility Functions (jpxUtils)

Function Purpose
throttle(fn, ms, trailing) Rate-limit function calls
secondsToTime(s, ms) Convert seconds to HH:MM:SS
daysSince(dateStr) Days since UTC date
getSortedArray(arr, fn, asc) Sort array by computed value
lowerFirst(str) Lowercase first character
titleCase(str) Title-case with camelCase splitting
sentenceCase(str) Sentence-case
matchAny(str, ...regexps) Test multiple regexes, return first match
parseValue(val) Parse string to number if numeric
isEmpty(obj) Check if object has no own properties
inRange(value, [min, max]) Range check
getValueByPath(obj, keys) Dot-path object access
deepMerge(target, source) Recursive object merge
getSortedKeys(order, keys) Sort keys by predefined order
inc(obj, key, step) Increment object property
createButton(container, opts) Create styled button with toast feedback
createToast(content, dur) Show toast notification
stringifyLimited(obj, level) Pretty-print JSON with depth limit
toRegExp(input) Parse regex string or create literal regex
captureKeyCombo(onComplete, onAbort) Listen for single key combo
formatKeyCombo(input, sep) Format key combo object/string for display
parseHVClasses(container) Decode HentaiVerse obfuscated class names
createTimeRecords() Factory for empty time records
createCombatRecords() Factory for empty combat records
createRevenueRecords() Factory for empty revenue records
xhrGet(urlArray, interval) Staggered XHR requests with Promise.allSettled

Summary Statistics

  • Total lines: 6,215
  • Named functions: ~45
  • Utility functions: 25
  • Regular expressions: ~55 defined in regExp object
  • Supported fighting styles: 10 (5 physical + 5 mage variants)
  • Supported battle types: 6 + "General" fallback
  • Total battle mode combinations: 60
  • Action types: 10
  • General condition keys: 25
  • Target condition keys: 12
  • Priority rules: 6
  • IndexedDB object store: 1 (battleRecords)
  • localStorage keys: ~20+
  • Singleton modules: 3 (PanelManager, Market, Utils)
  • Default market prices: ~80 items
  • Monster boss types: 30+ across 6 tiers
  • I18N entries: ~150+