- 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)
537 lines
19 KiB
Markdown
537 lines
19 KiB
Markdown
# 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:**
|
|
```javascript
|
|
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):**
|
|
```javascript
|
|
// 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:**
|
|
```javascript
|
|
// 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:**
|
|
```javascript
|
|
// Quickbar icons have cooldown info in their style/attributes
|
|
// Timer shows turns until available
|
|
```
|
|
|
|
**Battle log (post-combat):**
|
|
```javascript
|
|
// 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.**
|
|
|
|
```javascript
|
|
// 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
|
|
|
|
```javascript
|
|
// 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
|
|
|
|
```javascript
|
|
// 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
|
|
```javascript
|
|
// 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
|
|
```javascript
|
|
// 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
|
|
```javascript
|
|
// 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
|
|
```javascript
|
|
// 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
|
|
```javascript
|
|
// 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
|
|
```javascript
|
|
// 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**:
|
|
|
|
```javascript
|
|
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:
|
|
```javascript
|
|
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:
|
|
- [x] Require human input per combat round (hotkey press or mouse hover)
|
|
- [x] NOT auto-advance between rounds without input
|
|
- [x] NOT auto-start new battles
|
|
- [x] NOT auto-feed monsters
|
|
- [x] NOT auto-solve RiddleMaster
|
|
- [x] NOT combine multiple actions into one user input
|
|
- [x] Stop on RiddleMaster detection and alert user
|
|
- [x] 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
|
|
|
|
1. **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
|
|
|
|
2. **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
|
|
|
|
3. **Extract Monster Lab data** — build a JSON database of monster weaknesses from the existing script databases (decondelite's server at nibl.co.uk)
|
|
|
|
4. **Build MVP**: start with battle parser + strategy engine + hover system, integrating the best patterns from all existing tools
|
|
|
|
5. **Test in a low-level account** before running on your main to verify rules compliance
|