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==
|
||||
// @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';
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// CONFIG — default settings
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
const VERSION = '0.14.41';
|
||||
const VERSION = '0.15.0';
|
||||
|
||||
const CFG = {
|
||||
// — Battle automation
|
||||
|
|
|
|||
|
|
@ -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/*
|
||||
|
|
|
|||
Loading…
Reference in a new issue