v0.14.12 - Add captureAllTooltips() with async hover-to-popup delay
- captureAllTooltips() iterates each equipment slot, calls equips.set() to trigger the tooltip popup, waits 300ms for the DOM to update, then reads and saves the full stat block from #popup_box - Catches 'Popup Box' default text vs real tooltip - Saves stats directly to localStorage gear DB - Exposed as HV.captureTooltips() for console use - For best results: visit Character Equipment page, run HV.captureTooltips()
This commit is contained in:
parent
c632c13818
commit
ce09755fa8
6 changed files with 201 additions and 63 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name HV Unified
|
// @name HV Unified
|
||||||
// @namespace hvunified
|
// @namespace hvunified
|
||||||
// @version 0.14.11
|
// @version 0.14.12
|
||||||
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
||||||
// @author GaboGG + Hermes
|
// @author GaboGG + Hermes
|
||||||
// @match *://*.hentaiverse.org/*
|
// @match *://*.hentaiverse.org/*
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
// CONFIG — default settings
|
// CONFIG — default settings
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
const VERSION = '0.14.11';
|
const VERSION = '0.14.12';
|
||||||
|
|
||||||
const CFG = {
|
const CFG = {
|
||||||
// — Battle automation
|
// — Battle automation
|
||||||
|
|
@ -3442,18 +3442,17 @@ function scrapeCharacterEquip() {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Read the popup box for the currently-displayed tooltip
|
// Read the popup box for the currently-displayed tooltip
|
||||||
const tooltip = parseTooltipPopup();
|
// Note: popup_box only updates on actual hover; triggerAllTooltips
|
||||||
|
// may not work if equips.set is async. We still get names/IDs from slots.
|
||||||
// Merge tooltip stats with the matching item
|
try {
|
||||||
if (tooltip && tooltip.name) {
|
const tooltip = parseTooltipPopup();
|
||||||
const matchIdx = scraped.findIndex(s => tooltip.name.includes(s.name) || s.name.includes(tooltip.name));
|
if (tooltip && tooltip.name && tooltip.name !== 'Popup Box') {
|
||||||
if (matchIdx >= 0) {
|
const matchIdx = scraped.findIndex(s => tooltip.name.includes(s.name) || s.name.includes(tooltip.name));
|
||||||
scraped[matchIdx].stats = tooltip.stats;
|
if (matchIdx >= 0) {
|
||||||
} else if (scraped.length > 0) {
|
scraped[matchIdx].stats = tooltip.stats;
|
||||||
// If no match, attach to first item as extra info
|
}
|
||||||
scraped[0].tooltipStats = tooltip.stats;
|
|
||||||
}
|
}
|
||||||
}
|
} catch (e) {}
|
||||||
|
|
||||||
if (scraped.length > 0) {
|
if (scraped.length > 0) {
|
||||||
const db = getGearDB();
|
const db = getGearDB();
|
||||||
|
|
@ -3563,20 +3562,66 @@ function triggerAllTooltips() {
|
||||||
const omo = nameDiv ? (nameDiv.getAttribute('onmouseover') || '') : slot.getAttribute('onmouseover') || '';
|
const omo = nameDiv ? (nameDiv.getAttribute('onmouseover') || '') : slot.getAttribute('onmouseover') || '';
|
||||||
const idMatch = omo.match(/equips\.set\((\d+)/);
|
const idMatch = omo.match(/equips\.set\((\d+)/);
|
||||||
if (idMatch && typeof equips !== 'undefined' && equips.set) {
|
if (idMatch && typeof equips !== 'undefined' && equips.set) {
|
||||||
// Trigger the tooltip
|
try {
|
||||||
equips.set(parseInt(idMatch[1]), 'slot_pane', 250, 130);
|
equips.set(parseInt(idMatch[1]), 'slot_pane', 250, 130);
|
||||||
|
} catch (e) {}
|
||||||
// Read the popup
|
|
||||||
const tooltip = parseTooltipPopup();
|
|
||||||
if (tooltip && tooltip.name) {
|
|
||||||
results.push(tooltip);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Manual: hover each item and capture tooltip stats with delays
|
||||||
|
// Run this from console on the character equipment page for full stats
|
||||||
|
function captureAllTooltips(delay = 400) {
|
||||||
|
const slots = document.querySelectorAll('#eqsb > .eqb');
|
||||||
|
const results = [];
|
||||||
|
let idx = 0;
|
||||||
|
|
||||||
|
function next() {
|
||||||
|
if (idx >= slots.length) {
|
||||||
|
console.log(`%c[HV] Captured ${results.length} tooltips`, 'color:#0f0');
|
||||||
|
// Re-scrape with the captured data
|
||||||
|
autoScrapeGear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const slot = slots[idx];
|
||||||
|
const nameDiv = slot.querySelector(':scope > div[onmouseover]');
|
||||||
|
const omo = nameDiv ? (nameDiv.getAttribute('onmouseover') || '') : '';
|
||||||
|
const idMatch = omo.match(/equips\.set\((\d+)/);
|
||||||
|
|
||||||
|
if (idMatch && typeof equips !== 'undefined' && equips.set) {
|
||||||
|
try {
|
||||||
|
equips.set(parseInt(idMatch[1]), 'slot_pane', 250, 130);
|
||||||
|
// Wait for popup to populate
|
||||||
|
setTimeout(() => {
|
||||||
|
const tooltip = parseTooltipPopup();
|
||||||
|
if (tooltip && tooltip.name && tooltip.name !== 'Popup Box') {
|
||||||
|
// Store in localStorage directly
|
||||||
|
const db = getGearDB();
|
||||||
|
const match = db.find(e => e.id === idMatch[1]);
|
||||||
|
if (match) {
|
||||||
|
match.stats = tooltip.stats;
|
||||||
|
saveGearDB(db);
|
||||||
|
}
|
||||||
|
results.push(tooltip);
|
||||||
|
console.log(`%c[HV] [${idx}] ${tooltip.name}`, 'color:#888');
|
||||||
|
}
|
||||||
|
idx++;
|
||||||
|
next();
|
||||||
|
}, 300);
|
||||||
|
return;
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
idx++;
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
return 'Capturing tooltips... check console';
|
||||||
|
}
|
||||||
|
|
||||||
// ── Auto-detect and scrape current page ──
|
// ── Auto-detect and scrape current page ──
|
||||||
function autoScrapeGear() {
|
function autoScrapeGear() {
|
||||||
const url = window.location.href || '';
|
const url = window.location.href || '';
|
||||||
|
|
@ -3954,6 +3999,7 @@ window.HV = {
|
||||||
gearSummary: () => logGearSummary(),
|
gearSummary: () => logGearSummary(),
|
||||||
scrapeGear: () => autoScrapeGear(),
|
scrapeGear: () => autoScrapeGear(),
|
||||||
gearDebug: () => debugScrapeGear(),
|
gearDebug: () => debugScrapeGear(),
|
||||||
|
captureTooltips: () => captureAllTooltips(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name HV Unified
|
// @name HV Unified
|
||||||
// @namespace hvunified
|
// @namespace hvunified
|
||||||
// @version 0.14.11
|
// @version 0.14.12
|
||||||
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
||||||
// @author GaboGG + Hermes
|
// @author GaboGG + Hermes
|
||||||
// @match *://*.hentaiverse.org/*
|
// @match *://*.hentaiverse.org/*
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
// CONFIG — default settings
|
// CONFIG — default settings
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
const VERSION = '0.14.11';
|
const VERSION = '0.14.12';
|
||||||
|
|
||||||
const CFG = {
|
const CFG = {
|
||||||
// — Battle automation
|
// — Battle automation
|
||||||
|
|
@ -3442,18 +3442,17 @@ function scrapeCharacterEquip() {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Read the popup box for the currently-displayed tooltip
|
// Read the popup box for the currently-displayed tooltip
|
||||||
const tooltip = parseTooltipPopup();
|
// Note: popup_box only updates on actual hover; triggerAllTooltips
|
||||||
|
// may not work if equips.set is async. We still get names/IDs from slots.
|
||||||
// Merge tooltip stats with the matching item
|
try {
|
||||||
if (tooltip && tooltip.name) {
|
const tooltip = parseTooltipPopup();
|
||||||
const matchIdx = scraped.findIndex(s => tooltip.name.includes(s.name) || s.name.includes(tooltip.name));
|
if (tooltip && tooltip.name && tooltip.name !== 'Popup Box') {
|
||||||
if (matchIdx >= 0) {
|
const matchIdx = scraped.findIndex(s => tooltip.name.includes(s.name) || s.name.includes(tooltip.name));
|
||||||
scraped[matchIdx].stats = tooltip.stats;
|
if (matchIdx >= 0) {
|
||||||
} else if (scraped.length > 0) {
|
scraped[matchIdx].stats = tooltip.stats;
|
||||||
// If no match, attach to first item as extra info
|
}
|
||||||
scraped[0].tooltipStats = tooltip.stats;
|
|
||||||
}
|
}
|
||||||
}
|
} catch (e) {}
|
||||||
|
|
||||||
if (scraped.length > 0) {
|
if (scraped.length > 0) {
|
||||||
const db = getGearDB();
|
const db = getGearDB();
|
||||||
|
|
@ -3563,20 +3562,66 @@ function triggerAllTooltips() {
|
||||||
const omo = nameDiv ? (nameDiv.getAttribute('onmouseover') || '') : slot.getAttribute('onmouseover') || '';
|
const omo = nameDiv ? (nameDiv.getAttribute('onmouseover') || '') : slot.getAttribute('onmouseover') || '';
|
||||||
const idMatch = omo.match(/equips\.set\((\d+)/);
|
const idMatch = omo.match(/equips\.set\((\d+)/);
|
||||||
if (idMatch && typeof equips !== 'undefined' && equips.set) {
|
if (idMatch && typeof equips !== 'undefined' && equips.set) {
|
||||||
// Trigger the tooltip
|
try {
|
||||||
equips.set(parseInt(idMatch[1]), 'slot_pane', 250, 130);
|
equips.set(parseInt(idMatch[1]), 'slot_pane', 250, 130);
|
||||||
|
} catch (e) {}
|
||||||
// Read the popup
|
|
||||||
const tooltip = parseTooltipPopup();
|
|
||||||
if (tooltip && tooltip.name) {
|
|
||||||
results.push(tooltip);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Manual: hover each item and capture tooltip stats with delays
|
||||||
|
// Run this from console on the character equipment page for full stats
|
||||||
|
function captureAllTooltips(delay = 400) {
|
||||||
|
const slots = document.querySelectorAll('#eqsb > .eqb');
|
||||||
|
const results = [];
|
||||||
|
let idx = 0;
|
||||||
|
|
||||||
|
function next() {
|
||||||
|
if (idx >= slots.length) {
|
||||||
|
console.log(`%c[HV] Captured ${results.length} tooltips`, 'color:#0f0');
|
||||||
|
// Re-scrape with the captured data
|
||||||
|
autoScrapeGear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const slot = slots[idx];
|
||||||
|
const nameDiv = slot.querySelector(':scope > div[onmouseover]');
|
||||||
|
const omo = nameDiv ? (nameDiv.getAttribute('onmouseover') || '') : '';
|
||||||
|
const idMatch = omo.match(/equips\.set\((\d+)/);
|
||||||
|
|
||||||
|
if (idMatch && typeof equips !== 'undefined' && equips.set) {
|
||||||
|
try {
|
||||||
|
equips.set(parseInt(idMatch[1]), 'slot_pane', 250, 130);
|
||||||
|
// Wait for popup to populate
|
||||||
|
setTimeout(() => {
|
||||||
|
const tooltip = parseTooltipPopup();
|
||||||
|
if (tooltip && tooltip.name && tooltip.name !== 'Popup Box') {
|
||||||
|
// Store in localStorage directly
|
||||||
|
const db = getGearDB();
|
||||||
|
const match = db.find(e => e.id === idMatch[1]);
|
||||||
|
if (match) {
|
||||||
|
match.stats = tooltip.stats;
|
||||||
|
saveGearDB(db);
|
||||||
|
}
|
||||||
|
results.push(tooltip);
|
||||||
|
console.log(`%c[HV] [${idx}] ${tooltip.name}`, 'color:#888');
|
||||||
|
}
|
||||||
|
idx++;
|
||||||
|
next();
|
||||||
|
}, 300);
|
||||||
|
return;
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
idx++;
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
return 'Capturing tooltips... check console';
|
||||||
|
}
|
||||||
|
|
||||||
// ── Auto-detect and scrape current page ──
|
// ── Auto-detect and scrape current page ──
|
||||||
function autoScrapeGear() {
|
function autoScrapeGear() {
|
||||||
const url = window.location.href || '';
|
const url = window.location.href || '';
|
||||||
|
|
@ -3954,6 +3999,7 @@ window.HV = {
|
||||||
gearSummary: () => logGearSummary(),
|
gearSummary: () => logGearSummary(),
|
||||||
scrapeGear: () => autoScrapeGear(),
|
scrapeGear: () => autoScrapeGear(),
|
||||||
gearDebug: () => debugScrapeGear(),
|
gearDebug: () => debugScrapeGear(),
|
||||||
|
captureTooltips: () => captureAllTooltips(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// CONFIG — default settings
|
// CONFIG — default settings
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
const VERSION = '0.14.11';
|
const VERSION = '0.14.12';
|
||||||
|
|
||||||
const CFG = {
|
const CFG = {
|
||||||
// — Battle automation
|
// — Battle automation
|
||||||
|
|
|
||||||
|
|
@ -199,18 +199,17 @@ function scrapeCharacterEquip() {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Read the popup box for the currently-displayed tooltip
|
// Read the popup box for the currently-displayed tooltip
|
||||||
const tooltip = parseTooltipPopup();
|
// Note: popup_box only updates on actual hover; triggerAllTooltips
|
||||||
|
// may not work if equips.set is async. We still get names/IDs from slots.
|
||||||
// Merge tooltip stats with the matching item
|
try {
|
||||||
if (tooltip && tooltip.name) {
|
const tooltip = parseTooltipPopup();
|
||||||
const matchIdx = scraped.findIndex(s => tooltip.name.includes(s.name) || s.name.includes(tooltip.name));
|
if (tooltip && tooltip.name && tooltip.name !== 'Popup Box') {
|
||||||
if (matchIdx >= 0) {
|
const matchIdx = scraped.findIndex(s => tooltip.name.includes(s.name) || s.name.includes(tooltip.name));
|
||||||
scraped[matchIdx].stats = tooltip.stats;
|
if (matchIdx >= 0) {
|
||||||
} else if (scraped.length > 0) {
|
scraped[matchIdx].stats = tooltip.stats;
|
||||||
// If no match, attach to first item as extra info
|
}
|
||||||
scraped[0].tooltipStats = tooltip.stats;
|
|
||||||
}
|
}
|
||||||
}
|
} catch (e) {}
|
||||||
|
|
||||||
if (scraped.length > 0) {
|
if (scraped.length > 0) {
|
||||||
const db = getGearDB();
|
const db = getGearDB();
|
||||||
|
|
@ -320,20 +319,66 @@ function triggerAllTooltips() {
|
||||||
const omo = nameDiv ? (nameDiv.getAttribute('onmouseover') || '') : slot.getAttribute('onmouseover') || '';
|
const omo = nameDiv ? (nameDiv.getAttribute('onmouseover') || '') : slot.getAttribute('onmouseover') || '';
|
||||||
const idMatch = omo.match(/equips\.set\((\d+)/);
|
const idMatch = omo.match(/equips\.set\((\d+)/);
|
||||||
if (idMatch && typeof equips !== 'undefined' && equips.set) {
|
if (idMatch && typeof equips !== 'undefined' && equips.set) {
|
||||||
// Trigger the tooltip
|
try {
|
||||||
equips.set(parseInt(idMatch[1]), 'slot_pane', 250, 130);
|
equips.set(parseInt(idMatch[1]), 'slot_pane', 250, 130);
|
||||||
|
} catch (e) {}
|
||||||
// Read the popup
|
|
||||||
const tooltip = parseTooltipPopup();
|
|
||||||
if (tooltip && tooltip.name) {
|
|
||||||
results.push(tooltip);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Manual: hover each item and capture tooltip stats with delays
|
||||||
|
// Run this from console on the character equipment page for full stats
|
||||||
|
function captureAllTooltips(delay = 400) {
|
||||||
|
const slots = document.querySelectorAll('#eqsb > .eqb');
|
||||||
|
const results = [];
|
||||||
|
let idx = 0;
|
||||||
|
|
||||||
|
function next() {
|
||||||
|
if (idx >= slots.length) {
|
||||||
|
console.log(`%c[HV] Captured ${results.length} tooltips`, 'color:#0f0');
|
||||||
|
// Re-scrape with the captured data
|
||||||
|
autoScrapeGear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const slot = slots[idx];
|
||||||
|
const nameDiv = slot.querySelector(':scope > div[onmouseover]');
|
||||||
|
const omo = nameDiv ? (nameDiv.getAttribute('onmouseover') || '') : '';
|
||||||
|
const idMatch = omo.match(/equips\.set\((\d+)/);
|
||||||
|
|
||||||
|
if (idMatch && typeof equips !== 'undefined' && equips.set) {
|
||||||
|
try {
|
||||||
|
equips.set(parseInt(idMatch[1]), 'slot_pane', 250, 130);
|
||||||
|
// Wait for popup to populate
|
||||||
|
setTimeout(() => {
|
||||||
|
const tooltip = parseTooltipPopup();
|
||||||
|
if (tooltip && tooltip.name && tooltip.name !== 'Popup Box') {
|
||||||
|
// Store in localStorage directly
|
||||||
|
const db = getGearDB();
|
||||||
|
const match = db.find(e => e.id === idMatch[1]);
|
||||||
|
if (match) {
|
||||||
|
match.stats = tooltip.stats;
|
||||||
|
saveGearDB(db);
|
||||||
|
}
|
||||||
|
results.push(tooltip);
|
||||||
|
console.log(`%c[HV] [${idx}] ${tooltip.name}`, 'color:#888');
|
||||||
|
}
|
||||||
|
idx++;
|
||||||
|
next();
|
||||||
|
}, 300);
|
||||||
|
return;
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
idx++;
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
return 'Capturing tooltips... check console';
|
||||||
|
}
|
||||||
|
|
||||||
// ── Auto-detect and scrape current page ──
|
// ── Auto-detect and scrape current page ──
|
||||||
function autoScrapeGear() {
|
function autoScrapeGear() {
|
||||||
const url = window.location.href || '';
|
const url = window.location.href || '';
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name HV Unified
|
// @name HV Unified
|
||||||
// @namespace hvunified
|
// @namespace hvunified
|
||||||
// @version 0.14.11
|
// @version 0.14.12
|
||||||
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
||||||
// @author GaboGG + Hermes
|
// @author GaboGG + Hermes
|
||||||
// @match *://*.hentaiverse.org/*
|
// @match *://*.hentaiverse.org/*
|
||||||
|
|
|
||||||
|
|
@ -25,4 +25,5 @@ window.HV = {
|
||||||
gearSummary: () => logGearSummary(),
|
gearSummary: () => logGearSummary(),
|
||||||
scrapeGear: () => autoScrapeGear(),
|
scrapeGear: () => autoScrapeGear(),
|
||||||
gearDebug: () => debugScrapeGear(),
|
gearDebug: () => debugScrapeGear(),
|
||||||
|
captureTooltips: () => captureAllTooltips(),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue