- 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)
34 KiB
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:
-
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. -
Module Bootstrap (lines 463-465): Three singleton-style modules are immediately invoked:
jpxPanelManager()— creates/configures the control widget overlayjpxMarket()— market price fetcher/cacherjpxUtils()— 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_initflag on the function object itself. -
Entry Point (line 6215):
initDo()is called at file's end. -
initDo()(line 1242): The router function that:- Injects CSS via
<style id="jpx"> - Registers
beforeunloadhandler (storeTmp) for crash-safe state persistence - Registers
pointerdownhandler to dismiss multi-select popup panels - Registers
keydownhandler with throttledactionManager(75ms throttle) - Initializes I18N (
initDoI18n) - Page Detection via
document.querySelector('#textlog'):- If
#textlogexists ANDdoInitDoBattleis false →initDoBattle()— we're in a battle - If
#riddlemasterexists →riddleRecorder()— RiddleMaster encounter - If no
#textlog(Lobby) → reads player info (level, stamina, difficulty, persona, spell damage bonus) from DOM selectors and stores in localStorage
- If
- Injects CSS via
1.2 Hooking Into the Game
The script hooks into the game at three levels:
-
MutationObserver on
#textlog(line 1541): AMutationObserverwatcheslog.firstChildforchildListchanges. Every time new battle log text is added,preProcessLog()fires (throttled at 200ms, trailing). This is the primary event loop. -
DOM event listeners:
keydownondocument(capture phase, line 1255) — for hotkeys and key-bound battle actionspointerdownondocument(line 1249) — for dismissing multi-select panelsmousemoveonwindow(line 1366) — setsjpxPanelManager.ready = trueDOMContentLoadedforreDoBattle()on round transitions (line 1369)beforeunloadonwindow(line 1248) — callsstoreTmp()to persist state
-
Custom AJAX round advancement (lines 1623-1716): When
ajaxRoundis 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'swindow.Battleobject viadocument.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 styleslocalStorage['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 firstattacks— array of offensive actions evaluated secondkb_*(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 IsekaipLevel— Player level rangepMaxSpellType— Player's strongest spell elementbattleTypes— Battle type(s) (Arena, Encounter, etc.)difficulty— Difficulty arrayroundCurrent,roundLeft,roundTotal— Round positionfloor— Tower floor rangepActionCooldown— Cooldown range of specific actions (skill/spell/item)pActionCounts— Uses-per-round range of specific actionspHP,pMP,pSP— Player resource percentagespOC— Overcharge percentagepSpiritStatus— Spirit Stance active?pEffects— Player has effects with turns in rangepIgnoredEffects— Player does NOT have these effectspEffectStacks— Player effect stack countsmonsters,activeMonsters,defeatedMonsters— Monster countsbosses,activeBosses,defeatedBosses— Boss countsmLevel— Monster level rangemWithoutEffects— Count of monsters without specific effects
Target Conditions (conditionsTarget, lines 980-999): Checked per-monster with offset/matched mechanics:
tName— Regex-matchable monster nametTypes— Monster type (Normal, Rare, Legendary, etc.)tClasses— Monster class from MonsterDB (Arthropod, Avion, etc.)tPowerLevel— Monster power level rangetIndex— Monster position (0-based, negative from end)tHP,tMP,tSP— Monster resource percentagestEffects— Monster has specific effectstIgnoredEffects— Monster does NOT have specific effectstEffectStacks— Monster effect stackstDaysSinceUpdate— 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
successCountincrements 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,
targetLockedprevents re-evaluation until an action is successfully executed - Smart Debuff skips target phase:
smartDebuffactions 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 nametargetCount— 1, 2, or 3 monsters to hitbottomUp— 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 defeatedminMonstersLeft— Threshold for infinite casting when many monsters remain
Algorithm:
- Check cooldown and action count limits
- Calculate
startIndex/endIndexrange fromactiveMonstersbounds tailSkip: if positive, trim from end; if negative, limit to first N from start- First pass (targetCount ≥ 3): Find 3 consecutive alive + undebuffed monsters → cast on middle
- Second pass (targetCount ≥ 2): Find 2 consecutive alive + undebuffed → cast on edge-appropriate monster
- Third pass (targetCount ≥ 3, relaxed): Find 3 consecutive where middle is alive → cast on middle
- 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#vcpHTML (eachdiv= 0.5 OC after the first), handle half-segment viavcrclass
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]): Parseonmouseovertooltip withregExp.spellInfo→ extract name and cooldown. If element hasonclick, cooldown = 0 (ready). Otherwise:lastUse + initCooldown - currentTurn - Item cooldowns (
.bti3 > div): Parse withregExp.itemInfo→ map item ID to name viaitemMap. 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
onmouseovertooltip usingregExp.spellMatchwith 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:
- Calculates hidden turn deltas by comparing saved vs visible effect turns
- Parses battle log for effect gains/expirations (
regExp.effectGain,regExp.effectExpired,regExp.effectWear, etc.) - Applies turn decrement to hidden effects
- Special handling for elemental effects (Searing Skin, Freezing Limbs, etc.) and Coalesced Mana
- Prunes effects that would have expired
- Renders hidden effect icons into the
.btm6DOM
3.6 Monster Info (MonsterDB Integration, line 2404)
Polls window.HVMonsterDB.getCurrentMonstersInformation() with 250ms timeout for:
monsterClass— e.g., "Arthropod", "Daimon"attack— attack typeplvl— power levellastUpdate— timestamp fortDaysSinceUpdatecondition
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+ NOTRound 1 / 1→Arenarandom encounter→Encounterarena challenge+Round 1 / 1→ColosseumGrindfest→Battle1000Item World→ItemThe Tower→Tower(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
staminaRecordsin 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():
- Check for global keybind match (non-repeat only)
- If inactive and
#textlogexists and monsters are alive, check for mode-specific keybinding - Repeat keys use throttled
actionManager(75ms), initial press uses directactionManager
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
mouseEntertrigger for auto-battle activation - Dispatches
jpx_ctrlWidget_updateCustomEvent 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()withfieldRenderersregistry 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 databasestoreBattleRecords()— Save after battle completiongetBattleRecordsRender()— 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()comparingbattleVersion/statsVersion
8. COMPATIBILITY
8.1 Monsterbation (Monster DB Script) Integration
The script integrates with window.HVMonsterDB (Monsterbation's monster database):
monsterDBReady()(line 2404): Pollswindow.HVMonsterDB.getCurrentMonstersInformation()with 250ms timeout- Used for:
monsterClass,attacktype,plvl(power level),lastUpdatedate tClasses,tPowerLevel,tDaysSinceUpdateconditions depend on this datashowMonsterInfooption 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
regExpobject - 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+