- 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)
19 KiB
HentaiVerse Scripts — Source Code Analysis & Architecture Patterns
Based on Monsterbation 1.4.1.2 (163KB, 2402 lines) + Wiki formulas
=============================================================================
1. SOURCE CODE STATUS
| Script | Version | Status | Source |
|---|---|---|---|
| Monsterbation | 1.4.1.2 | Downloaded (163KB) | Forum attachment 157304 |
| CrunkJuice | 1.3.0 | Available (same thread) | Forum attachment (companion) |
| jpx | v2026.07.06 | Forum thread 290507 | GitHub-hosted? (Cloudflare blocked) |
| HV Utils | 4.2.3 | Forum thread 211883 | Forum attachment 188793 (Cloudflare blocked) |
| HV Toolbox | 1.0.14 | Forum thread 209070 | Forum attachment 165038 (Cloudflare blocked) |
| HVSTAT | 5.7.1 | Forum thread 79552 | Forum attachment 15604 (Cloudflare blocked) |
Blockers: E-hentai forums are behind Cloudflare protection. Direct downloads from the VPS are blocked by JS challenge. None are on GreasyFork/GitHub (community convention is forum-only distribution). User needs to download these from their browser and upload to VPS.
=============================================================================
2. MONSTERBATION — ARCHITECTURE DEEP DIVE
2.1 High-Level Architecture
Monsterbation = Tampermonkey userscript injected into hentaiverse.org
DETECTION: Check for DOM elements #textlog or #riddlemaster → battle page
NO HTTP requests for game state. ALL state parsed from DOM.
PATTERN: Page interactions = clicking existing DOM elements.
NEVER constructs raw HTTP POSTs.
2.2 Core Loop
Page loads (battle round start)
→ Enhance() called
→ Parse DOM: monsters[], vitals (HP/MP/SP bars), buffs, cooldowns
→ Set up event listeners on monsters (mouseover, mousedown, wheel, contextmenu)
→ If hover enabled: auto-fire hover attack on target monster
→ Wait for human interaction (keyboard/mouse)
Human moves mouse over monster
→ SetTarget(i) called
→ If hover enabled: Hover() fires
→ Hover(): run cfg.hoverAction() → then monsters[target].click()
→ The click triggers the page's built-in onclick handler
→ Server processes turn → new HTML page loads → loop repeats
2.3 DOM-Based State Parsing
Monster detection:
monsters = document.querySelectorAll('.btm') // Battle table monsters
// Each monster has onclick attribute when alive
monsters[i].hasAttribute('onclick') // true = alive, false = dead
Vitals (HP/MP/SP):
// Parse from bar image widths (2 possible formats - isekai vs persistent)
mp = parseInt(image.width) / 414 // MP bar fill ratio
sp = parseInt(image.width) / 414 // SP bar fill ratio
hp_left = parseInt(image.width) / 414 // HP bar fill ratio
// Overcharge: bar_orange.png width
// Isekai uses /207 instead of /414 (different base widths)
Buffs/Debuffs:
// Parse from onmouseover text on effect icons
var effects = document.getElementById('pane_effects').getElementsByTagName('img')
effects[n].getAttribute('onmouseover') // Contains duration + stack count
// Regex: /regexp.duration/ extracts turns remaining and stacks
Cooldowns:
// Quickbar icons have cooldown info in their style/attributes
// Timer shows turns until available
Battle log (post-combat):
// Parses textLog for damage numbers, proficiency gains, drops
var log = document.getElementById('textlog')
// Regex extracts: monsters present, damage dealt, items dropped
2.4 Action System (how it submits turns)
Design principle: NEVER construct URLs or form POSTs. ALWAYS click page elements.
// SPELLS: Find the spell icon by its onmouseover text containing spell name
function Cast(name) {
return function() {
var spell;
if ((spell = document.querySelector(
'.bts > div[onclick][onmouseover*="\'' + name + '\'"]'))) {
// Use a dummy element to trigger the spell's mouseover+click
dummy.setAttribute('onclick', spell.getAttribute('onmouseover'));
dummy.click();
spell.click();
}
};
}
// ITEMS: Find by element ID
function Use(id) {
return function() {
var item;
if ((item = document.getElementById('ikey_' + id))) {
dummy.setAttribute('onclick', item.getAttribute('onmouseover'));
dummy.click();
item.click();
}
};
}
// ATTACK: Click the monster div directly
function TargetMonster(num) {
return function() {
if (monsters[num] && monsters[num].hasAttribute('onclick'))
monsters[num].click();
};
}
// TOGGLE: Click checkbox elements by ID
function Toggle(name) {
return function() {
if ((state = document.getElementById('ckey_' + name.toLowerCase())))
state.click();
};
}
Key insight: The game's page has HTML elements with onclick/onmouseover handlers. Monsterbation finds these elements and triggers their events. The server processes the resulting HTTP request normally. This is why it's considered "single action" — each click = one server round-trip = one turn.
2.5 Hover System (the killer feature)
hoverArea: 6 = which .btmX div within monster triggers hover
Hover flow:
1. Mouse enters monster area → SetTarget(i) called
2. If hover enabled → Hover() fires
3. Hover() → runs the configured action chain
4. Action chain clicks spell/item/attack elements on the page
5. The last action clicks the monster → server resolves turn
6. Next page loads → Enhance() → parse new state → loop
Modifier keys:
- No modifier: cfg.hoverAction
- Shift: cfg.hoverShiftAction (e.g. dark spells)
- Ctrl: cfg.hoverCtrlAction (e.g. holy spells)
- Alt: cfg.hoverAltAction (e.g. fire spells)
Hover interrupts:
- stopOnSpark (spark of life triggered)
- stopOnUsable (consumable becomes available)
- stopOnLowHP/MP/SP
- stopOnBuffsExpiring (custom regex)
- stopOnMiss
- Impulse system: inject one-time spell into rotation
2.6 Spell Rotation System
// Strongest: tries actions in order, first one that is available fires
Strongest([Cast('Ragnarok'), Cast('Disintegrate'), Cast('Corruption')])
// This tries Ragnarok first. If the element doesn't exist (cooldown/spell unavailable),
// it falls through to Disintegrate, then Corruption.
// The action fires by finding the spell icon in the DOM and clicking it.
// Impulse: injects a one-time action into the rotation
// e.g. press I while hovering to cast Imperil once, then resume normal rotation
Bind(KEY_I, Impulse(Cast('Imperil')))
2.7 Keybinding System
// Format: Bind(KEY, ON_KEYDOWN, ON_KEYUP)
// KEY: string like 'F', 'Digit1', 'Numpad1', 'ShiftLeft', etc.
// Examples:
Bind('KeyR', Strongest([Cast('Ragnarok'), Cast('Disintegrate')]));
// Press R → cast strongest dark spell at mouse target
Bind('KeyH', Any, ToggleHover);
// Release H → toggle hover play
Bind('KeyV', HoverAction(Nothing));
// Hold V → attack monster at mouse cursor
Bind('Digit2', Strongest([ToggleHover, HoverAction(Cast('Imperil'))]));
// Press 2 → cast Imperil (even if hovering is stopped)
2.8 Profile System
cfg.name → cfg.persona[0].name → cfg.persona[0].set[0].name
├── settings ├── settings
├── hoverAction ├── hoverAction
├── bind ├── bind
└── ... └── ...
Profiles auto-switch when:
- Persona changes (cfg.profileAutoswitch)
- Equipment set changes
- Isekai mode detected (cfg.isekaiInherit)
Storage: localStorage['HVmbcfg'] for config, localStorage['HVtrackdrops'] for tracking
2.9 Out-of-Battle Features (CrunkJuice, the companion script)
CrunkJuice handles non-combat automation:
- ED confirm (auto-confirm that annoying dialog)
- Faster "sell all" button
- Monster morale/hunger display
- Feed pills/crystals to all monsters
- Monster database search
- RE timer/counter
- Quality filter in bazaar
- Arena page auto-open
=============================================================================
3. CORE ARCHITECTURAL PATTERNS (applicable to any new tool)
Pattern 1: DOM Detection, Not HTTP
// Detect battle page:
if (document.getElementById('textlog') || document.getElementById('riddlemaster')) {
// We're in battle
}
// Detect bazaar/character page:
if (document.getElementById('mainpane')) {
// We're in the main game area
}
Pattern 2: Click Elements, Don't POST
// WRONG (bot-like, detectable):
fetch('?s=BATTLE', {method:'POST', body:'action=attack&target=2'});
// RIGHT (same as human clicking):
document.querySelector('.btm[onclick]').click();
document.querySelector('.bts > div[onclick]').click();
Pattern 3: Element Selection by Content
// Find the Imperil spell icon:
document.querySelector('.bts > div[onclick][onmouseover*="Imperil"]')
// Find a specific item by its game-internal ID:
document.getElementById('ikey_3') // Item slot 3
// Find Spirit Stance toggle:
document.getElementById('ckey_spirit')
Pattern 4: State from Style Widths
// HP percentage from green bar:
var hp_pct = parseInt(
document.querySelector('img[src$="green.png"]').style.width
) / 414;
// MP from blue bar:
var mp_pct = parseInt(
document.querySelector('img[src$="blue.png"]').style.width
) / 414;
// Overcharge from orange bar:
var oc_full = parseInt(
document.querySelector('img[src$="bar_orange.png"]').style.width
) >= 414;
Pattern 5: localStorage for Persistence
// Config storage:
localStorage['HVmbcfg'] = JSON.stringify(config);
// Monster HP database (to show HP numbers):
localStorage['HVmonsterData'] = JSON.stringify(monsterData);
// Drop tracking:
localStorage['HVtrackdrops'] = JSON.stringify(droplog);
// Combat stats:
localStorage['HVcombatlog'] = JSON.stringify(combatlog);
Pattern 6: Dummy Element Trick
// The game often requires a specific click sequence:
// 1. Click spell icon → triggers mouseover → shows targeting mode
// 2. Click monster → actually casts spell
// Monsterbation handles this with a dummy element:
var dummy = document.createElement('div');
dummy.setAttribute('onclick', spell.getAttribute('onmouseover'));
dummy.click(); // Sets up targeting
spell.click(); // Actually selects spell
monster.click(); // Fires the spell at target
=============================================================================
4. EXISTING TOOL GAPS — WHERE TO IMPROVE
Gap 1: No automated Battle Mode Selection
Current tools require manually picking Arena/Grindfest/Item World. A tool could:
- Parse available arenas, check which ones are uncleared that day
- Sort by credit/minute efficiency based on player's clear speed
- One-click "do today's best arena"
Gap 2: No Smart Mana/SP Management
Current hover systems use threshold-based interrupts (stop at X% mana). A smarter approach:
- Calculate exact mana cost of next spell (including interference, spirit stance, etc.)
- Predict regeneration rate over time
- Decide: cast now vs wait 1 tick vs use mana potion
- Pre-cast buffs when MP is high, conserve when MP is low
Gap 3: No Per-Monster Adaptive Targeting
Current tools target whoever the mouse is over, or use "Strongest spell rotation" blindly. A fully informed targeting system would:
- Scan all monsters for resistances (from Monster Lab database)
- Match elemental weakness to available spells
- Prioritize: debuff needs → lowest HP (sweep) → highest threat
- Account for monster status effects (don't waste Imperil on already-imperiled target)
Gap 4: No Auto-Equipment Optimizer for Battle
Current tools don't read equipment loadouts and suggest optimal gear. Could:
- Parse equipment stats for current fighting style
- Auto-equip best set for the battle mode (Arena vs GF vs IW)
- Warn about broken equipment before battle
Gap 5: Fragmented Tool Ecosystem
Monsterbation (battle) + CrunkJuice (out-of-battle) + HV Utils (shop/shrine) + HV Toolbox (MoogleMail) + jpx (auto-battler rules) = 5+ separate scripts. A unified tool with modular architecture would:
- Share state between modules (drops in battle → auto-sell in shop)
- Single config/profile system
- Consistent keybindings and UI
Gap 6: No VPS-Ready Remote Monitoring
None of the tools offer headless monitoring. Could:
- WebSocket bridge: browser extension sends state to VPS
- VPS logs: daily earnings, Hath balance, stamina status
- Push notifications: "Your stamina is full" or "Arena reset is ready"
Gap 7: Training Queue
Current tools handle training manually. Could:
- Queue training purchases with credit thresholds
- Auto-buy Scavenger/Quartermaster/Archaeologist when credits exceed threshold
- Prioritize Ability Boost when leveling
Gap 8: Monster Lab Auto-Management
CrunkJuice has basic feeding. Could extend to:
- Optimal crystal allocation (which monster gets which crystal type)
- Morale management (pill when below threshold)
- Auto-name/auto-level tracking
=============================================================================
5. BUILD PLAN — UNIFIED BATTLE ASSISTANT (Tampermonkey)
Architecture
hv-unified.user.js
├── engine/
│ ├── detector.js ← Detect which page we're on (battle/bazaar/monsterlab/etc.)
│ ├── parser.js ← DOM→gameState (shared parser for all modules)
│ ├── submitter.js ← Action submitter (click elements, dummy trick)
│ ├── storage.js ← localStorage wrapper (config, tracking, monster DB)
│ └── config.js ← Profile system, keybinding parser
├── battle/
│ ├── vitals.js ← HP/MP/SP/OC monitoring
│ ├── buffs.js ← Buff duration tracking, alert conditions
│ ├── cooldowns.js ← Spell/skill/item cooldown display
│ ├── monsters.js ← Monster state (HP, status, resistances)
│ ├── hover.js ← Hover system (target selection, action execution)
│ ├── strategy.js ← Action decision logic (spell rotation, target priority)
│ ├── spells.js ← Spell selection (find icons by name, check mana cost)
│ ├── skills.js ← Skill selection (overcharge management)
│ └── items.js ← Item usage (potions, scrolls, infusions)
├── bazaar/
│ ├── arena.js ← Arena detection, completion tracking
│ ├── shrine.js ← Bulk shrine artifacts
│ ├── shop.js ← Auto-sell/salvage low-quality gear
│ ├── training.js ← Training queue management
│ └── monsterlab.js ← Auto-feeding, crystal allocation
├── tracking/
│ ├── drops.js ← Drop logging and statistics
│ ├── damage.js ← Damage dealt/taken tracking
│ ├── proficiency.js ← Proficiency gain tracking
│ └── credits.js ← Credit earning rate calculation
└── ui/
├── quickbar.js ← Extended quickbar with cooldowns
├── overlay.js ← Settings panel, profile switcher
├── alerts.js ← Visual/audio alerts (low HP, spark, buffs expiring)
└── keybind.js ← Keybinding system
Strategy Engine — The Core Innovation
Rather than Monsterbation's simple "cast all spells in rotation at whatever target", build a conditional rule engine:
const strategy = {
// Priority-ordered rules. First matching rule fires.
rules: [
{
name: 'Emergency heal',
condition: () => state.hp < 0.25,
action: () => Use('health_potion') || Cast('Full-Cure') || Cast('Cure'),
},
{
name: 'Maintain Imperil',
condition: () => !monsterHasEffect(currentTarget, 'imperil'),
action: () => Cast('Imperil'),
},
{
name: 'Maintain Weaken',
condition: () => !monsterHasEffect(currentTarget, 'weaken') && state.mp > 0.3,
action: () => Cast('Weaken'),
},
{
name: 'Maintain buffs',
condition: () => !hasBuff('haste') || !hasBuff('shadow_veil'),
action: () => castMissingBuffs(),
},
{
name: 'Use Spirit Stance',
condition: () => state.oc >= 100 && !spiritStanceActive && state.sp > 0.25,
action: () => Toggle('spirit'),
},
{
name: 'Use skill at full OC',
condition: () => state.oc >= 200 && !skillOnCooldown('frenzied_blows'),
action: () => Skill('Frenzied Blows'),
},
{
name: 'AOE spell',
condition: () => aliveMonsters > 1 && canCast('Inferno'),
action: () => Cast('Inferno'),
},
{
name: 'Single target: match weakness',
condition: () => true, // fallback
action: () => castBestSpellAgainst(currentTarget),
},
],
};
Elemental Weakness Matching
Build a monster database:
const monsterDB = {
// key: monster name pattern
'Giant': { weak: 'Cold', resist: 'Fire', hp_scaling: 1.2 },
'Dragon': { weak: 'Elec', resist: 'Fire', hp_scaling: 1.5 },
// ... populated from Monster Lab scans + community data
};
function castBestSpellAgainst(monster) {
const info = findMonsterInDB(monster.name);
if (info && info.weak) {
return Cast(getHighestTierSpell(info.weak));
}
// Fallback: use neutral/highest damage spell
return Cast('Ragnarok');
}
=============================================================================
6. RULES COMPLIANCE CHECKLIST
The script MUST:
- Require human input per combat round (hotkey press or mouse hover)
- NOT auto-advance between rounds without input
- NOT auto-start new battles
- NOT auto-feed monsters
- NOT auto-solve RiddleMaster
- NOT combine multiple actions into one user input
- Stop on RiddleMaster detection and alert user
- Parse game state only for recommendations, not for triggering actions
For out-of-battle features:
- Auto-sell/salvage: ALLOWED if user initiates (HV Utils pattern)
- Auto-training queue: GREY AREA — training is manual, queuing probably OK
- Auto-shrine: ALLOWED if user clicks the button (bulk shrine OK)
- Monster feeding: NOT ALLOWED if automatic (must require button press)
- Auto-arena selection: GREY AREA — suggesting which arena is OK, auto-entering is NOT
=============================================================================
7. NEXT STEPS
-
User downloads remaining scripts from forums (browser → upload to VPS):
- jpx from https://forums.e-hentai.org/index.php?showtopic=290507
- HV Utils from https://forums.e-hentai.org/index.php?showtopic=211883
- HV Toolbox from https://forums.e-hentai.org/index.php?showtopic=209070
-
Analyze jpx's ruleset system — it has the most advanced conditional logic for auto-battle, and understanding its rule format would inform our strategy engine design
-
Extract Monster Lab data — build a JSON database of monster weaknesses from the existing script databases (decondelite's server at nibl.co.uk)
-
Build MVP: start with battle parser + strategy engine + hover system, integrating the best patterns from all existing tools
-
Test in a low-level account before running on your main to verify rules compliance