hv-unified/references/hv-scripts-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

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

  1. User downloads remaining scripts from forums (browser → upload to VPS):

  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