From 1c14e9b2a4a57681aa3b24047d45ecd74a9052fe Mon Sep 17 00:00:00 2001 From: GaboGG Date: Fri, 31 Jul 2026 14:24:25 -0400 Subject: [PATCH] v0.15.0 - Keep Top 5 smart-selector for sell/salvage screens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New button on Sell and Salvage tabs: '⭐ Keep Top 5 / Select Sell/Salvage Rest' - Groups every un-equipped, unlocked, unpinned item into 19 categories: 1H, 2H, Staff, Shield + 5 armor slots (Head/Body/Hands/Legs/Feet) × armor type (Cloth/Light/Heavy) - Scores items using the same gear-analysis weights (quality mult, weapon type bonus, PMit/Evade/STR etc.) - Keeps the 5 highest-scoring items per category unchecked - Checks everything else for selling or salvaging - Console logs per-category breakdown (e.g. '🗂 Light Head: 12 items → keep 5 (top 161/149/141/138/131), select 7') Bump: 0.14.41 → 0.15.0 --- scripts/hv-unified.user.js | 78 +++++++++++++++++++++++++++++++++++++- scripts/latest.user.js | 78 +++++++++++++++++++++++++++++++++++++- src/armory.js | 74 ++++++++++++++++++++++++++++++++++++ src/config.js | 2 +- src/header.user.js | 2 +- 5 files changed, 228 insertions(+), 6 deletions(-) diff --git a/scripts/hv-unified.user.js b/scripts/hv-unified.user.js index 82abab6..bd73ff1 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.41 +// @version 0.15.0 // @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.41'; +const VERSION = '0.15.0'; const CFG = { // — Battle automation @@ -3280,6 +3280,80 @@ function enhanceArmory() { toolbar.appendChild(b); } + // ── Keep Top 5 per category (score-based), select the rest ── + // Categories: 1H, 2H, Staff, Shield + 5 armor slots × (Cloth, Light, Heavy) + function qualityIndex(name) { + const q = KB.qualities.find(qq => name.includes(qq)); + return q ? KB.qualities.indexOf(q) : 0; + } + + function keepTop5AndSelect(action) { + const catMap = new Map(); + + items.forEach(item => { + if (item.equipped || item.locked || item.pinned) return; + const data = getHVEquipData(item.id); + const stats = data && data.d ? parseEquipHTML(data.d) : null; + if (!stats) return; // skip unscraped items — leave untouched + + const type = (stats.type || ''); + let cat = '?'; + if (type.includes('Two-handed')) cat = '2H'; + else if (type.includes('One-handed')) cat = '1H'; + else if (type.includes('Staff')) cat = 'Staff'; + else if (type.includes('Shield')) cat = 'Shield'; + else if (type.includes('Cloth')) cat = 'Cloth'; + else if (type.includes('Light')) cat = 'Light'; + else if (type.includes('Heavy')) cat = 'Heavy'; + + // For armor, append the slot (Head/Body/Hands/Legs/Feet) + if (['Cloth', 'Light', 'Heavy'].includes(cat)) { + const slot = gearDetectSlot({ name: item.name, category: cat }); + if (!slot) return; + cat += ' ' + slot; + } + + const isWeaponCat = cat.startsWith('1H') || cat.startsWith('2H') || cat === 'Staff' || cat === 'Shield'; + const quality = qualityIndex(item.name); + const score = isWeaponCat + ? gearScoreWeapon({ name: item.name, stats, quality }) + : gearScoreArmor({ name: item.name, stats, quality }); + + if (!catMap.has(cat)) catMap.set(cat, []); + catMap.get(cat).push({ item, score }); + }); + + // Keep top 5 per category, select the rest + let selected = 0; + catMap.forEach((list, cat) => { + list.sort((a, b) => b.score - a.score); + const keep = list.slice(0, 5); + const dump = list.slice(5); + dump.forEach(({ item }) => { + item.checkbox.checked = true; + selected++; + }); + console.log(`%c[HV] 🗂 ${cat}: ${list.length} items → keep ${keep.length} (top ${keep.map(k => k.score.toFixed(0)).join('/')}), select ${dump.length}`, 'color:#0f0'); + }); + + if (typeof update_selected_count === 'function') update_selected_count(); + return selected; + } + + // ── Keep Top 5 button — available on sell and salvage screens ── + if (screen === 'sell' || screen === 'salvage') { + const b3 = document.createElement('input'); + b3.type = 'button'; + b3.value = `⭐ Keep Top 5 / Select ${screen === 'sell' ? 'Sell' : 'Salvage'} Rest`; + b3.style.cssText = 'padding:2px 6px;cursor:pointer;background:#7a5a2a;color:#fff;border:none;border-radius:3px;font-size:10px'; + b3.title = 'Keeps the 5 highest-scoring items in each category (1H, 2H, Staff, Shield, and each armor slot per Cloth/Light/Heavy). Selects everything else.'; + b3.onclick = () => { + const n = keepTop5AndSelect(screen); + console.log(`%c[HV] ⭐ Keep Top 5: ${n} items selected for ${screen}`, 'color:#0f0'); + }; + toolbar.appendChild(b3); + } + // Clear button always available const clear = document.createElement('input'); clear.type = 'button'; diff --git a/scripts/latest.user.js b/scripts/latest.user.js index 82abab6..bd73ff1 100644 --- a/scripts/latest.user.js +++ b/scripts/latest.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name HV Unified // @namespace hvunified -// @version 0.14.41 +// @version 0.15.0 // @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.41'; +const VERSION = '0.15.0'; const CFG = { // — Battle automation @@ -3280,6 +3280,80 @@ function enhanceArmory() { toolbar.appendChild(b); } + // ── Keep Top 5 per category (score-based), select the rest ── + // Categories: 1H, 2H, Staff, Shield + 5 armor slots × (Cloth, Light, Heavy) + function qualityIndex(name) { + const q = KB.qualities.find(qq => name.includes(qq)); + return q ? KB.qualities.indexOf(q) : 0; + } + + function keepTop5AndSelect(action) { + const catMap = new Map(); + + items.forEach(item => { + if (item.equipped || item.locked || item.pinned) return; + const data = getHVEquipData(item.id); + const stats = data && data.d ? parseEquipHTML(data.d) : null; + if (!stats) return; // skip unscraped items — leave untouched + + const type = (stats.type || ''); + let cat = '?'; + if (type.includes('Two-handed')) cat = '2H'; + else if (type.includes('One-handed')) cat = '1H'; + else if (type.includes('Staff')) cat = 'Staff'; + else if (type.includes('Shield')) cat = 'Shield'; + else if (type.includes('Cloth')) cat = 'Cloth'; + else if (type.includes('Light')) cat = 'Light'; + else if (type.includes('Heavy')) cat = 'Heavy'; + + // For armor, append the slot (Head/Body/Hands/Legs/Feet) + if (['Cloth', 'Light', 'Heavy'].includes(cat)) { + const slot = gearDetectSlot({ name: item.name, category: cat }); + if (!slot) return; + cat += ' ' + slot; + } + + const isWeaponCat = cat.startsWith('1H') || cat.startsWith('2H') || cat === 'Staff' || cat === 'Shield'; + const quality = qualityIndex(item.name); + const score = isWeaponCat + ? gearScoreWeapon({ name: item.name, stats, quality }) + : gearScoreArmor({ name: item.name, stats, quality }); + + if (!catMap.has(cat)) catMap.set(cat, []); + catMap.get(cat).push({ item, score }); + }); + + // Keep top 5 per category, select the rest + let selected = 0; + catMap.forEach((list, cat) => { + list.sort((a, b) => b.score - a.score); + const keep = list.slice(0, 5); + const dump = list.slice(5); + dump.forEach(({ item }) => { + item.checkbox.checked = true; + selected++; + }); + console.log(`%c[HV] 🗂 ${cat}: ${list.length} items → keep ${keep.length} (top ${keep.map(k => k.score.toFixed(0)).join('/')}), select ${dump.length}`, 'color:#0f0'); + }); + + if (typeof update_selected_count === 'function') update_selected_count(); + return selected; + } + + // ── Keep Top 5 button — available on sell and salvage screens ── + if (screen === 'sell' || screen === 'salvage') { + const b3 = document.createElement('input'); + b3.type = 'button'; + b3.value = `⭐ Keep Top 5 / Select ${screen === 'sell' ? 'Sell' : 'Salvage'} Rest`; + b3.style.cssText = 'padding:2px 6px;cursor:pointer;background:#7a5a2a;color:#fff;border:none;border-radius:3px;font-size:10px'; + b3.title = 'Keeps the 5 highest-scoring items in each category (1H, 2H, Staff, Shield, and each armor slot per Cloth/Light/Heavy). Selects everything else.'; + b3.onclick = () => { + const n = keepTop5AndSelect(screen); + console.log(`%c[HV] ⭐ Keep Top 5: ${n} items selected for ${screen}`, 'color:#0f0'); + }; + toolbar.appendChild(b3); + } + // Clear button always available const clear = document.createElement('input'); clear.type = 'button'; diff --git a/src/armory.js b/src/armory.js index 433619a..52efb8c 100644 --- a/src/armory.js +++ b/src/armory.js @@ -174,6 +174,80 @@ function enhanceArmory() { toolbar.appendChild(b); } + // ── Keep Top 5 per category (score-based), select the rest ── + // Categories: 1H, 2H, Staff, Shield + 5 armor slots × (Cloth, Light, Heavy) + function qualityIndex(name) { + const q = KB.qualities.find(qq => name.includes(qq)); + return q ? KB.qualities.indexOf(q) : 0; + } + + function keepTop5AndSelect(action) { + const catMap = new Map(); + + items.forEach(item => { + if (item.equipped || item.locked || item.pinned) return; + const data = getHVEquipData(item.id); + const stats = data && data.d ? parseEquipHTML(data.d) : null; + if (!stats) return; // skip unscraped items — leave untouched + + const type = (stats.type || ''); + let cat = '?'; + if (type.includes('Two-handed')) cat = '2H'; + else if (type.includes('One-handed')) cat = '1H'; + else if (type.includes('Staff')) cat = 'Staff'; + else if (type.includes('Shield')) cat = 'Shield'; + else if (type.includes('Cloth')) cat = 'Cloth'; + else if (type.includes('Light')) cat = 'Light'; + else if (type.includes('Heavy')) cat = 'Heavy'; + + // For armor, append the slot (Head/Body/Hands/Legs/Feet) + if (['Cloth', 'Light', 'Heavy'].includes(cat)) { + const slot = gearDetectSlot({ name: item.name, category: cat }); + if (!slot) return; + cat += ' ' + slot; + } + + const isWeaponCat = cat.startsWith('1H') || cat.startsWith('2H') || cat === 'Staff' || cat === 'Shield'; + const quality = qualityIndex(item.name); + const score = isWeaponCat + ? gearScoreWeapon({ name: item.name, stats, quality }) + : gearScoreArmor({ name: item.name, stats, quality }); + + if (!catMap.has(cat)) catMap.set(cat, []); + catMap.get(cat).push({ item, score }); + }); + + // Keep top 5 per category, select the rest + let selected = 0; + catMap.forEach((list, cat) => { + list.sort((a, b) => b.score - a.score); + const keep = list.slice(0, 5); + const dump = list.slice(5); + dump.forEach(({ item }) => { + item.checkbox.checked = true; + selected++; + }); + console.log(`%c[HV] 🗂 ${cat}: ${list.length} items → keep ${keep.length} (top ${keep.map(k => k.score.toFixed(0)).join('/')}), select ${dump.length}`, 'color:#0f0'); + }); + + if (typeof update_selected_count === 'function') update_selected_count(); + return selected; + } + + // ── Keep Top 5 button — available on sell and salvage screens ── + if (screen === 'sell' || screen === 'salvage') { + const b3 = document.createElement('input'); + b3.type = 'button'; + b3.value = `⭐ Keep Top 5 / Select ${screen === 'sell' ? 'Sell' : 'Salvage'} Rest`; + b3.style.cssText = 'padding:2px 6px;cursor:pointer;background:#7a5a2a;color:#fff;border:none;border-radius:3px;font-size:10px'; + b3.title = 'Keeps the 5 highest-scoring items in each category (1H, 2H, Staff, Shield, and each armor slot per Cloth/Light/Heavy). Selects everything else.'; + b3.onclick = () => { + const n = keepTop5AndSelect(screen); + console.log(`%c[HV] ⭐ Keep Top 5: ${n} items selected for ${screen}`, 'color:#0f0'); + }; + toolbar.appendChild(b3); + } + // Clear button always available const clear = document.createElement('input'); clear.type = 'button'; diff --git a/src/config.js b/src/config.js index 4b45d09..005be34 100644 --- a/src/config.js +++ b/src/config.js @@ -2,7 +2,7 @@ // CONFIG — default settings // ═══════════════════════════════════════════════════════════════════════ -const VERSION = '0.14.41'; +const VERSION = '0.15.0'; const CFG = { // — Battle automation diff --git a/src/header.user.js b/src/header.user.js index c96a0eb..48779e3 100644 --- a/src/header.user.js +++ b/src/header.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name HV Unified // @namespace hvunified -// @version 0.14.41 +// @version 0.15.0 // @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse // @author GaboGG + Hermes // @match *://*.hentaiverse.org/*