diff --git a/scripts/hv-unified.user.js b/scripts/hv-unified.user.js index 4d16e6a..f6432b2 100644 --- a/scripts/hv-unified.user.js +++ b/scripts/hv-unified.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name HV Unified // @namespace hvunified -// @version 0.14.15 +// @version 0.14.16 // @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse // @author GaboGG + Hermes // @match *://*.hentaiverse.org/* @@ -17,7 +17,7 @@ // CONFIG — default settings // ═══════════════════════════════════════════════════════════════════════ -const VERSION = '0.14.15'; +const VERSION = '0.14.16'; const CFG = { // — Battle automation @@ -3401,37 +3401,64 @@ function scrapeCharacterEquip() { function scrapeArmory() { const scraped = []; const el = document.getElementById('equiplist'); - if (!el) return scraped; - let cat = ''; - el.querySelectorAll('table tr').forEach(row => { - if (row.classList.contains('eqtplabel')) { cat = (row.textContent || '').trim(); return; } - if (row.classList.contains('eqselall')) return; - const omo = row.getAttribute('onmouseover') || ''; - const idM = omo.match(/hover_equip\((\d+)\)/); - const cb = row.querySelector('input[name="eqids[]"]'); - const itemId = idM ? idM[1] : (cb ? cb.value : ''); - const label = row.querySelector('label'); - let name = label ? (label.textContent || '').trim() : ''; - if (cb && label) name = (label.textContent || '').replace(cb.outerHTML, '').trim(); - if (!itemId && !name) return; - - const obj = { id: itemId, name: name || 'Unknown', category: cat, source: 'armory', scrapedAt: Date.now() }; - if (itemId) { - const data = getHVEquipData(itemId); - if (data) { - if (data.t) obj.name = data.t; - if (data.q != null) obj.quality = data.q; - if (data.d) obj.stats = parseEquipHTML(data.d); + // First, scrape any items visible in the current filter tab + if (el) { + let cat = ''; + el.querySelectorAll('table tr').forEach(row => { + if (row.classList.contains('eqtplabel')) { cat = (row.textContent || '').trim(); return; } + if (row.classList.contains('eqselall')) return; + const omo = row.getAttribute('onmouseover') || ''; + const idM = omo.match(/hover_equip\((\d+)\)/); + const cb = row.querySelector('input[name="eqids[]"]'); + const itemId = idM ? idM[1] : (cb ? cb.value : ''); + const label = row.querySelector('label'); + let name = label ? (label.textContent || '').trim() : ''; + if (cb && label) name = (label.textContent || '').replace(cb.outerHTML, '').trim(); + if (!itemId && !name) return; + const obj = { id: itemId, name: name || 'Unknown', category: cat, source: 'armory', scrapedAt: Date.now() }; + if (itemId) { + const data = getHVEquipData(itemId); + if (data) { + if (data.t) obj.name = data.t; + if (data.q != null) obj.quality = data.q; + if (data.d) obj.stats = parseEquipHTML(data.d); + } } - } - scraped.push(obj); - }); + scraped.push(obj); + }); + } + + // Second, scan the full store for items NOT in the visible list + // This catches all inventory regardless of which filter tab you're on + const store = getHVEquipStore(); + if (store) { + const visibleIds = new Set(scraped.map(s => s.id)); + Object.entries(store).forEach(([id, data]) => { + if (visibleIds.has(id)) return; // already scraped from DOM + const stats = data.d ? parseEquipHTML(data.d) : null; + // Try to determine category from stats type + let category = 'All Items'; + if (stats && stats.type) { + if (stats.type.includes('Cloth')) category = 'Cloth Armor'; + else if (stats.type.includes('Light')) category = 'Light Armor'; + else if (stats.type.includes('Heavy')) category = 'Heavy Armor'; + else if (stats.type.includes('Shield')) category = 'Shield'; + else if (stats.type.includes('Staff')) category = 'Staff'; + else if (stats.type.includes('Axe') || stats.type.includes('Mace') || stats.type.includes('Sword') || stats.type.includes('Estoc') || stats.type.includes('Rapier') || stats.type.includes('Club') || stats.type.includes('Dagger')) category = 'Weapon'; + } + if (!stats || (!data.t && !data.d)) return; // skip empty entries + scraped.push({ + id, name: data.t || 'Unknown', category, + quality: data.q, stats, source: 'armory', scrapedAt: Date.now() + }); + }); + } if (scraped.length > 0) { const db = getGearDB(); saveGearDB([...db.filter(e => e.source !== 'armory'), ...scraped]); - console.log(`%c[HV] 📦 Armory: ${scraped.length} items (${scraped.filter(s => s.stats).length} with stats)`, 'color:#0f0'); + console.log(`%c[HV] 📦 Armory: ${scraped.length} items (${scraped.filter(s => s.stats).length} with stats, ${scraped.length - (el ? el.querySelectorAll('table tr[onmouseover]').length : 0)} from store)`, 'color:#0f0'); } return scraped; } diff --git a/scripts/latest.user.js b/scripts/latest.user.js index 4d16e6a..f6432b2 100644 --- a/scripts/latest.user.js +++ b/scripts/latest.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name HV Unified // @namespace hvunified -// @version 0.14.15 +// @version 0.14.16 // @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse // @author GaboGG + Hermes // @match *://*.hentaiverse.org/* @@ -17,7 +17,7 @@ // CONFIG — default settings // ═══════════════════════════════════════════════════════════════════════ -const VERSION = '0.14.15'; +const VERSION = '0.14.16'; const CFG = { // — Battle automation @@ -3401,37 +3401,64 @@ function scrapeCharacterEquip() { function scrapeArmory() { const scraped = []; const el = document.getElementById('equiplist'); - if (!el) return scraped; - let cat = ''; - el.querySelectorAll('table tr').forEach(row => { - if (row.classList.contains('eqtplabel')) { cat = (row.textContent || '').trim(); return; } - if (row.classList.contains('eqselall')) return; - const omo = row.getAttribute('onmouseover') || ''; - const idM = omo.match(/hover_equip\((\d+)\)/); - const cb = row.querySelector('input[name="eqids[]"]'); - const itemId = idM ? idM[1] : (cb ? cb.value : ''); - const label = row.querySelector('label'); - let name = label ? (label.textContent || '').trim() : ''; - if (cb && label) name = (label.textContent || '').replace(cb.outerHTML, '').trim(); - if (!itemId && !name) return; - - const obj = { id: itemId, name: name || 'Unknown', category: cat, source: 'armory', scrapedAt: Date.now() }; - if (itemId) { - const data = getHVEquipData(itemId); - if (data) { - if (data.t) obj.name = data.t; - if (data.q != null) obj.quality = data.q; - if (data.d) obj.stats = parseEquipHTML(data.d); + // First, scrape any items visible in the current filter tab + if (el) { + let cat = ''; + el.querySelectorAll('table tr').forEach(row => { + if (row.classList.contains('eqtplabel')) { cat = (row.textContent || '').trim(); return; } + if (row.classList.contains('eqselall')) return; + const omo = row.getAttribute('onmouseover') || ''; + const idM = omo.match(/hover_equip\((\d+)\)/); + const cb = row.querySelector('input[name="eqids[]"]'); + const itemId = idM ? idM[1] : (cb ? cb.value : ''); + const label = row.querySelector('label'); + let name = label ? (label.textContent || '').trim() : ''; + if (cb && label) name = (label.textContent || '').replace(cb.outerHTML, '').trim(); + if (!itemId && !name) return; + const obj = { id: itemId, name: name || 'Unknown', category: cat, source: 'armory', scrapedAt: Date.now() }; + if (itemId) { + const data = getHVEquipData(itemId); + if (data) { + if (data.t) obj.name = data.t; + if (data.q != null) obj.quality = data.q; + if (data.d) obj.stats = parseEquipHTML(data.d); + } } - } - scraped.push(obj); - }); + scraped.push(obj); + }); + } + + // Second, scan the full store for items NOT in the visible list + // This catches all inventory regardless of which filter tab you're on + const store = getHVEquipStore(); + if (store) { + const visibleIds = new Set(scraped.map(s => s.id)); + Object.entries(store).forEach(([id, data]) => { + if (visibleIds.has(id)) return; // already scraped from DOM + const stats = data.d ? parseEquipHTML(data.d) : null; + // Try to determine category from stats type + let category = 'All Items'; + if (stats && stats.type) { + if (stats.type.includes('Cloth')) category = 'Cloth Armor'; + else if (stats.type.includes('Light')) category = 'Light Armor'; + else if (stats.type.includes('Heavy')) category = 'Heavy Armor'; + else if (stats.type.includes('Shield')) category = 'Shield'; + else if (stats.type.includes('Staff')) category = 'Staff'; + else if (stats.type.includes('Axe') || stats.type.includes('Mace') || stats.type.includes('Sword') || stats.type.includes('Estoc') || stats.type.includes('Rapier') || stats.type.includes('Club') || stats.type.includes('Dagger')) category = 'Weapon'; + } + if (!stats || (!data.t && !data.d)) return; // skip empty entries + scraped.push({ + id, name: data.t || 'Unknown', category, + quality: data.q, stats, source: 'armory', scrapedAt: Date.now() + }); + }); + } if (scraped.length > 0) { const db = getGearDB(); saveGearDB([...db.filter(e => e.source !== 'armory'), ...scraped]); - console.log(`%c[HV] 📦 Armory: ${scraped.length} items (${scraped.filter(s => s.stats).length} with stats)`, 'color:#0f0'); + console.log(`%c[HV] 📦 Armory: ${scraped.length} items (${scraped.filter(s => s.stats).length} with stats, ${scraped.length - (el ? el.querySelectorAll('table tr[onmouseover]').length : 0)} from store)`, 'color:#0f0'); } return scraped; } diff --git a/src/config.js b/src/config.js index ae36b79..cddff5c 100644 --- a/src/config.js +++ b/src/config.js @@ -2,7 +2,7 @@ // CONFIG — default settings // ═══════════════════════════════════════════════════════════════════════ -const VERSION = '0.14.15'; +const VERSION = '0.14.16'; const CFG = { // — Battle automation diff --git a/src/gear-scraper.js b/src/gear-scraper.js index aefddd6..a2c7b4c 100644 --- a/src/gear-scraper.js +++ b/src/gear-scraper.js @@ -158,37 +158,64 @@ function scrapeCharacterEquip() { function scrapeArmory() { const scraped = []; const el = document.getElementById('equiplist'); - if (!el) return scraped; - let cat = ''; - el.querySelectorAll('table tr').forEach(row => { - if (row.classList.contains('eqtplabel')) { cat = (row.textContent || '').trim(); return; } - if (row.classList.contains('eqselall')) return; - const omo = row.getAttribute('onmouseover') || ''; - const idM = omo.match(/hover_equip\((\d+)\)/); - const cb = row.querySelector('input[name="eqids[]"]'); - const itemId = idM ? idM[1] : (cb ? cb.value : ''); - const label = row.querySelector('label'); - let name = label ? (label.textContent || '').trim() : ''; - if (cb && label) name = (label.textContent || '').replace(cb.outerHTML, '').trim(); - if (!itemId && !name) return; - - const obj = { id: itemId, name: name || 'Unknown', category: cat, source: 'armory', scrapedAt: Date.now() }; - if (itemId) { - const data = getHVEquipData(itemId); - if (data) { - if (data.t) obj.name = data.t; - if (data.q != null) obj.quality = data.q; - if (data.d) obj.stats = parseEquipHTML(data.d); + // First, scrape any items visible in the current filter tab + if (el) { + let cat = ''; + el.querySelectorAll('table tr').forEach(row => { + if (row.classList.contains('eqtplabel')) { cat = (row.textContent || '').trim(); return; } + if (row.classList.contains('eqselall')) return; + const omo = row.getAttribute('onmouseover') || ''; + const idM = omo.match(/hover_equip\((\d+)\)/); + const cb = row.querySelector('input[name="eqids[]"]'); + const itemId = idM ? idM[1] : (cb ? cb.value : ''); + const label = row.querySelector('label'); + let name = label ? (label.textContent || '').trim() : ''; + if (cb && label) name = (label.textContent || '').replace(cb.outerHTML, '').trim(); + if (!itemId && !name) return; + const obj = { id: itemId, name: name || 'Unknown', category: cat, source: 'armory', scrapedAt: Date.now() }; + if (itemId) { + const data = getHVEquipData(itemId); + if (data) { + if (data.t) obj.name = data.t; + if (data.q != null) obj.quality = data.q; + if (data.d) obj.stats = parseEquipHTML(data.d); + } } - } - scraped.push(obj); - }); + scraped.push(obj); + }); + } + + // Second, scan the full store for items NOT in the visible list + // This catches all inventory regardless of which filter tab you're on + const store = getHVEquipStore(); + if (store) { + const visibleIds = new Set(scraped.map(s => s.id)); + Object.entries(store).forEach(([id, data]) => { + if (visibleIds.has(id)) return; // already scraped from DOM + const stats = data.d ? parseEquipHTML(data.d) : null; + // Try to determine category from stats type + let category = 'All Items'; + if (stats && stats.type) { + if (stats.type.includes('Cloth')) category = 'Cloth Armor'; + else if (stats.type.includes('Light')) category = 'Light Armor'; + else if (stats.type.includes('Heavy')) category = 'Heavy Armor'; + else if (stats.type.includes('Shield')) category = 'Shield'; + else if (stats.type.includes('Staff')) category = 'Staff'; + else if (stats.type.includes('Axe') || stats.type.includes('Mace') || stats.type.includes('Sword') || stats.type.includes('Estoc') || stats.type.includes('Rapier') || stats.type.includes('Club') || stats.type.includes('Dagger')) category = 'Weapon'; + } + if (!stats || (!data.t && !data.d)) return; // skip empty entries + scraped.push({ + id, name: data.t || 'Unknown', category, + quality: data.q, stats, source: 'armory', scrapedAt: Date.now() + }); + }); + } if (scraped.length > 0) { const db = getGearDB(); saveGearDB([...db.filter(e => e.source !== 'armory'), ...scraped]); - console.log(`%c[HV] 📦 Armory: ${scraped.length} items (${scraped.filter(s => s.stats).length} with stats)`, 'color:#0f0'); + console.log(`%c[HV] 📦 Armory: ${scraped.length} items (${scraped.filter(s => s.stats).length} with stats, ${scraped.length - (el ? el.querySelectorAll('table tr[onmouseover]').length : 0)} from store)`, 'color:#0f0'); } return scraped; } diff --git a/src/header.user.js b/src/header.user.js index 3cafdae..a8a6570 100644 --- a/src/header.user.js +++ b/src/header.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name HV Unified // @namespace hvunified -// @version 0.14.15 +// @version 0.14.16 // @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse // @author GaboGG + Hermes // @match *://*.hentaiverse.org/*