v0.15.0 - Keep Top 5 smart-selector for sell/salvage screens
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
This commit is contained in:
parent
e935e34ae1
commit
1c14e9b2a4
5 changed files with 228 additions and 6 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name HV Unified
|
// @name HV Unified
|
||||||
// @namespace hvunified
|
// @namespace hvunified
|
||||||
// @version 0.14.41
|
// @version 0.15.0
|
||||||
// @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.41';
|
const VERSION = '0.15.0';
|
||||||
|
|
||||||
const CFG = {
|
const CFG = {
|
||||||
// — Battle automation
|
// — Battle automation
|
||||||
|
|
@ -3280,6 +3280,80 @@ function enhanceArmory() {
|
||||||
toolbar.appendChild(b);
|
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
|
// Clear button always available
|
||||||
const clear = document.createElement('input');
|
const clear = document.createElement('input');
|
||||||
clear.type = 'button';
|
clear.type = 'button';
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name HV Unified
|
// @name HV Unified
|
||||||
// @namespace hvunified
|
// @namespace hvunified
|
||||||
// @version 0.14.41
|
// @version 0.15.0
|
||||||
// @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.41';
|
const VERSION = '0.15.0';
|
||||||
|
|
||||||
const CFG = {
|
const CFG = {
|
||||||
// — Battle automation
|
// — Battle automation
|
||||||
|
|
@ -3280,6 +3280,80 @@ function enhanceArmory() {
|
||||||
toolbar.appendChild(b);
|
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
|
// Clear button always available
|
||||||
const clear = document.createElement('input');
|
const clear = document.createElement('input');
|
||||||
clear.type = 'button';
|
clear.type = 'button';
|
||||||
|
|
|
||||||
|
|
@ -174,6 +174,80 @@ function enhanceArmory() {
|
||||||
toolbar.appendChild(b);
|
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
|
// Clear button always available
|
||||||
const clear = document.createElement('input');
|
const clear = document.createElement('input');
|
||||||
clear.type = 'button';
|
clear.type = 'button';
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// CONFIG — default settings
|
// CONFIG — default settings
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
const VERSION = '0.14.41';
|
const VERSION = '0.15.0';
|
||||||
|
|
||||||
const CFG = {
|
const CFG = {
|
||||||
// — Battle automation
|
// — Battle automation
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name HV Unified
|
// @name HV Unified
|
||||||
// @namespace hvunified
|
// @namespace hvunified
|
||||||
// @version 0.14.41
|
// @version 0.15.0
|
||||||
// @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/*
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue