hv-unified/src/state.js
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

140 lines
3.7 KiB
JavaScript

// ═══════════════════════════════════════════════════════════════════════
// STATE — global runtime state
// ═══════════════════════════════════════════════════════════════════════
const SP = 'hvunified_';
const STATE = {
page: null,
level: 1,
tier: 'novice',
isekai: false,
battleMode: '',
inBattle: false,
battleInitialized: false,
hoverTarget: -1,
interruptHover: false,
interruptAlert: false,
monsters: [],
hp: 1,
mp: 1,
sp: 1,
oc: 0,
spiritStance: false,
buffs: {},
spellsKnown: [],
skillsKnown: [],
itemsKnown: {},
cooldowns: {},
battleMonsterNames: [],
channeling: false,
monsterData: {},
difficulty: 'Normal',
battleStats: {
damageDealt: 0,
damageTaken: 0,
turns: 0,
drops: 0,
credits: 0,
},
round: 0,
settingsVisible: false,
};
// Load monster data from localStorage
try {
STATE.monsterData = JSON.parse(localStorage[SP + 'monsters'] || '{}');
} catch (e) {
STATE.monsterData = {};
}
// ── helpers ──
function loadConfig() {
try {
const saved = JSON.parse(localStorage[SP + 'cfg'] || '{}');
for (const [k, v] of Object.entries(saved)) {
if (CFG[k] !== undefined) CFG[k] = v;
}
} catch (e) {}
}
function saveConfig() {
try { localStorage[SP + 'cfg'] = JSON.stringify(CFG); } catch (e) {}
}
function setConfig(key, value) {
if (CFG[key] !== undefined) {
CFG[key] = value;
saveConfig();
}
}
function readCSSDigits(container) {
if (!container) return null;
const digits = [];
container.querySelectorAll('div').forEach(c => {
const m = (c.className || '').match(/c4(\d)\b/);
if (m) digits.push(m[1]);
});
if (digits.length === 0) return null;
const parsed = parseInt(digits.join(''));
return isNaN(parsed) ? null : parsed;
}
// CSS font character map: c5a=a, c5b=b, ..., c5z=z
// c59 = space, c4a = period, c40-c49 = 0-9
// Note: no \\b word boundary — JS handles boundaries differently in class names
function readCSSText(container) {
if (!container) return '';
let text = '';
container.querySelectorAll('div').forEach(c => {
const cls = c.className || '';
// c5a-c5z → letters a-z
const letterMatch = cls.match(/c5([a-z])\b/);
if (letterMatch) {
text += letterMatch[1];
return;
}
// c59 → space
if (/c59\b/.test(cls)) {
text += ' ';
return;
}
// c4a → period
if (/c4a\b/.test(cls)) {
text += '.';
return;
}
// c40-c49 → digits 0-9
const digitMatch = cls.match(/c4([0-9])\b/);
if (digitMatch) {
text += digitMatch[1];
return;
}
});
return text;
}
// Save difficulty from last overworld page read
function cacheDifficulty() {
if (STATE.difficulty && STATE.difficulty !== 'Normal') {
try { localStorage[SP + 'difficulty'] = STATE.difficulty; } catch (e) {}
}
}
// Load cached difficulty (for battle pages where difficulty isn't in DOM)
function restoreCachedDifficulty() {
if (STATE.difficulty === 'Normal' || !STATE.difficulty) {
try {
const cached = localStorage[SP + 'difficulty'];
if (cached) STATE.difficulty = cached;
} catch (e) {}
}
}