v0.13.1 - Smart Armory, removed broken sell/salvage buttons
- Removed enhanceEquipShop() — broken bulk sell/salvage from equip shop page
- Rewrote enhanceArmory() with actionable equipment management panel:
- Parses all equipment from the Armory (ss=am) page
- Grades each piece by quality: Keep (Magnificent+), Keep (Superior),
Salvage (Average-Fair), Sell (Crude)
- Bulk Sell / Bulk Salvage buttons that click native game buttons
- Shows slot separators, quality colors, status icons (equipped/locked/stored)
- Sorting: equipped/pinned first, then by quality, then by level
- Summary counts for each action category
This commit is contained in:
parent
2107527c72
commit
1669167373
5 changed files with 427 additions and 238 deletions
|
|
@ -1,7 +1,7 @@
|
|||
// ==UserScript==
|
||||
// @name HV Unified
|
||||
// @namespace hvunified
|
||||
// @version 0.12.0
|
||||
// @version 0.13.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.12.0';
|
||||
const VERSION = '0.13.0';
|
||||
|
||||
const CFG = {
|
||||
// — Battle automation
|
||||
|
|
@ -1769,54 +1769,6 @@ function enhanceItemShop() {
|
|||
});
|
||||
}
|
||||
|
||||
// ── Equip Shop: quick sell/salvage buttons ──
|
||||
|
||||
function enhanceEquipShop() {
|
||||
if (!CFG.autoSell) return;
|
||||
const m = document.getElementById('mainpane');
|
||||
if (!m || document.getElementById('hv-quick-sell')) return;
|
||||
|
||||
const r = document.createElement('div');
|
||||
r.id = 'hv-quick-sell';
|
||||
r.style.cssText = 'margin:6px;display:flex;gap:6px;flex-wrap:wrap';
|
||||
|
||||
['Crude', 'Fair', 'Average', 'Superior'].forEach(q => {
|
||||
const b = document.createElement('input');
|
||||
b.type = 'button';
|
||||
b.value = `🔻 Sell ≤${q}`;
|
||||
b.style.cssText = 'padding:3px 8px;cursor:pointer;background:#d50c2d;color:#fff;border:none;border-radius:3px;font-size:11px';
|
||||
b.onclick = () => {
|
||||
$$('tr', m).forEach(row => {
|
||||
const t = (row.textContent || '');
|
||||
const qi = ['Crude', 'Fair', 'Average', 'Superior'].indexOf(q);
|
||||
if (['Crude', 'Fair', 'Average', 'Superior'].some((ql, i) => i <= qi && t.includes(ql)) && t.includes('Sell')) {
|
||||
const sb = row.querySelector('input[value="Sell"]');
|
||||
if (sb) sb.click();
|
||||
}
|
||||
});
|
||||
};
|
||||
r.appendChild(b);
|
||||
});
|
||||
|
||||
const s = document.createElement('input');
|
||||
s.type = 'button';
|
||||
s.value = '♻ Salvage ≤Average';
|
||||
s.style.cssText = 'padding:3px 8px;cursor:pointer;background:#555;color:#fff;border:none;border-radius:3px;font-size:11px';
|
||||
s.onclick = () => {
|
||||
$$('tr', m).forEach(row => {
|
||||
const t = (row.textContent || '');
|
||||
if ((t.includes('Crude') || t.includes('Fair') || t.includes('Average')) && t.includes('Salvage')) {
|
||||
const sb = row.querySelector('input[value="Salvage"]');
|
||||
if (sb) sb.click();
|
||||
}
|
||||
});
|
||||
};
|
||||
r.appendChild(s);
|
||||
|
||||
const ta = m.querySelector('div');
|
||||
if (ta) ta.insertBefore(r, ta.firstChild);
|
||||
}
|
||||
|
||||
// ── Equip advice: KEEP/SELL tags ──
|
||||
|
||||
function evaluateEquipment(text) {
|
||||
|
|
@ -2654,66 +2606,168 @@ function enhanceAbilities() {
|
|||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// ARMORY — equipment management panel
|
||||
// ARMORY — smart equipment inventory management
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
// Analyse equipment quality and assign a grade
|
||||
function gradeEquipment(name) {
|
||||
const q = KB.qualities.find(q => name.includes(q));
|
||||
if (!q) return { action: 'keep', label: '❓', color: '#888', reason: 'Unknown' };
|
||||
const qi = KB.qualities.indexOf(q);
|
||||
// Magnificent+ (index >= 5): always keep
|
||||
if (qi >= 5) return { action: 'keep', label: '✅ Keep', color: '#0f0', reason: `${q}+` };
|
||||
// Superior (index 3): borderline — keep if it's a weapon/armor we need
|
||||
if (qi === 3) return { action: 'keep', label: '⬆ Keep', color: '#fdcb00', reason: 'Superior' };
|
||||
// Average/Fair (1-2): salvage for materials
|
||||
if (qi >= 1) return { action: 'salvage', label: '♻ Salvage', color: '#888', reason: q };
|
||||
// Crude (0): sell
|
||||
return { action: 'sell', label: '💰 Sell', color: '#f80', reason: 'Crude' };
|
||||
}
|
||||
|
||||
function enhanceArmory() {
|
||||
const main = document.getElementById('mainpane');
|
||||
if (!main || document.getElementById('hv-armory')) return;
|
||||
if (!main || document.getElementById('hv-armory-panel')) return;
|
||||
|
||||
// ── Parse equipment from the page ──
|
||||
const items = [];
|
||||
let currentCategory = '';
|
||||
|
||||
$$('tr', main).forEach(row => {
|
||||
// Category label rows (Weapon, Armor sections)
|
||||
if (row.className === 'eqtplabel') {
|
||||
currentCategory = (row.textContent || '').trim();
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the equip item link/ID
|
||||
const links = row.querySelectorAll('a[href*="set_equip"]');
|
||||
if (links.length === 0) return;
|
||||
|
||||
// Item has an onmouseover for tooltip
|
||||
const omo = row.getAttribute('onmouseover') || '';
|
||||
const idMatch = omo.match(/hover_equip\((\d+)\)/);
|
||||
if (!idMatch) return;
|
||||
|
||||
const id = idMatch[1];
|
||||
const label = row.querySelector('label');
|
||||
if (!label) return;
|
||||
const name = (label.textContent || '').trim();
|
||||
const name = label ? (label.textContent || '').trim() : '';
|
||||
|
||||
// Detect level — HV renders Lv with CSS font in a .btm1 inside the row
|
||||
const levelDiv = row.querySelector('.btm1 label, .btm1');
|
||||
const levelText = levelDiv ? readCSSText(levelDiv) : '';
|
||||
const lvMatch = levelText.match(/lv\.?\s*(\d+)/i);
|
||||
const itemLevel = lvMatch ? parseInt(lvMatch[1]) : 0;
|
||||
|
||||
// Detect status indicators
|
||||
const rowHtml = row.innerHTML || '';
|
||||
const isLocked = rowHtml.includes('lock.png') || rowHtml.includes('lck.png');
|
||||
const isEquipped = rowHtml.includes('eqp.png') || rowHtml.includes('Equipped');
|
||||
const isPinned = rowHtml.includes('pin.png');
|
||||
const isStored = rowHtml.includes('store.png');
|
||||
|
||||
// Check for Sell/Salvage buttons in this row
|
||||
const sellBtn = row.querySelector('input[value="Sell"], a[onclick*="sell_equip"]');
|
||||
const salvBtn = row.querySelector('input[value="Salvage"], a[onclick*="salvage_equip"]');
|
||||
|
||||
const quality = KB.qualities.find(q => name.includes(q)) || '?';
|
||||
items.push({ id: idMatch[1], name, quality, slot: currentCategory });
|
||||
items.push({
|
||||
id, name, quality, slot: currentCategory,
|
||||
level: itemLevel,
|
||||
locked: isLocked, equipped: isEquipped,
|
||||
pinned: isPinned, stored: isStored,
|
||||
canSell: !!sellBtn, canSalvage: !!salvBtn,
|
||||
row,
|
||||
});
|
||||
});
|
||||
|
||||
if (items.length === 0) return;
|
||||
|
||||
// ── Grade each item ──
|
||||
const graded = items.map(item => ({
|
||||
...item,
|
||||
grade: gradeEquipment(item.name, item.slot),
|
||||
}));
|
||||
|
||||
// ── Sorting: equipped/pinned first, then by quality descending, then by level ──
|
||||
const qwords = KB.qualities;
|
||||
const best = (arr) => arr.sort((a, b) => qwords.indexOf(b.quality) - qwords.indexOf(a.quality))[0];
|
||||
const weapons = items.filter(i => !i.slot.includes('Armor') && !i.slot.includes('Shield'));
|
||||
const armors = items.filter(i => i.slot.includes('Armor') || i.slot.includes('Shoes'));
|
||||
const bestWeapon = best(weapons);
|
||||
const bestArmor = best(armors);
|
||||
const qColor = (q) => { const i = qwords.indexOf(q); return i >= 4 ? '#0f0' : i >= 2 ? '#fdcb00' : '#f80'; };
|
||||
graded.sort((a, b) => {
|
||||
if (a.equipped && !b.equipped) return -1;
|
||||
if (!a.equipped && b.equipped) return 1;
|
||||
if (a.pinned && !b.pinned) return -1;
|
||||
if (!a.pinned && b.pinned) return 1;
|
||||
const qDiff = (qwords.indexOf(b.quality) - qwords.indexOf(a.quality));
|
||||
if (qDiff !== 0) return qDiff;
|
||||
return b.level - a.level;
|
||||
});
|
||||
|
||||
let body = '';
|
||||
if (bestWeapon) body += `<div><span style="color:${qColor(bestWeapon.quality)}">${bestWeapon.quality}</span> <b>${bestWeapon.name}</b> ← best</div>`;
|
||||
if (bestArmor) body += `<div><span style="color:${qColor(bestArmor.quality)}">${bestArmor.quality}</span> <b>${bestArmor.name}</b> ← best</div>`;
|
||||
// ── Summary counts ──
|
||||
const keepCount = graded.filter(i => i.grade.action === 'keep').length;
|
||||
const sellCount = graded.filter(i => i.grade.action === 'sell').length;
|
||||
const salvCount = graded.filter(i => i.grade.action === 'salvage').length;
|
||||
const equippedCount = graded.filter(i => i.equipped).length;
|
||||
|
||||
body += '<div style="border-top:1px solid #374151;margin:6px 0"></div>';
|
||||
// ── Build bulk action buttons ──
|
||||
function bulkSell() {
|
||||
const toSell = graded.filter(i => i.grade.action === 'sell' && i.canSell && !i.equipped && !i.locked);
|
||||
if (toSell.length === 0) return;
|
||||
toSell.forEach(item => {
|
||||
const btn = item.row.querySelector('input[value="Sell"], a[onclick*="sell_equip"]');
|
||||
if (btn) btn.click();
|
||||
});
|
||||
}
|
||||
|
||||
function bulkSalvage() {
|
||||
const toSalv = graded.filter(i => i.grade.action === 'salvage' && i.canSalvage && !i.equipped && !i.locked);
|
||||
if (toSalv.length === 0) return;
|
||||
toSalv.forEach(item => {
|
||||
const btn = item.row.querySelector('input[value="Salvage"], a[onclick*="salvage_equip"]');
|
||||
if (btn) btn.click();
|
||||
});
|
||||
}
|
||||
|
||||
// ── Build the panel ──
|
||||
const qColor = (q) => { const i = qwords.indexOf(q); return i >= 5 ? '#0f0' : i >= 3 ? '#fdcb00' : i >= 1 ? '#888' : '#f80'; };
|
||||
|
||||
let body = `<div style="display:flex;gap:4px;margin-bottom:6px">
|
||||
<input type="button" value="💰 Sell Crude (${sellCount})" style="padding:3px 8px;cursor:pointer;background:#d50c2d;color:#fff;border:none;border-radius:3px;font-size:10px" id="hv-armory-bulk-sell">
|
||||
<input type="button" value="♻ Salvage ≤Fair (${salvCount})" style="padding:3px 8px;cursor:pointer;background:#555;color:#fff;border:none;border-radius:3px;font-size:10px" id="hv-armory-bulk-salvage">
|
||||
</div>`;
|
||||
|
||||
body += `<div style="border-top:1px solid #374151;margin:4px 0;padding:4px 0;font-size:9px;color:#888">
|
||||
✅ Keep: ${keepCount} | ♻ Salvage: ${salvCount} | 💰 Sell: ${sellCount} | 🗡 Equipped: ${equippedCount}
|
||||
</div>`;
|
||||
|
||||
// ── Table ──
|
||||
body += '<table style="width:100%;font-size:9px;border-collapse:collapse">';
|
||||
body += '<tr style="color:#888"><th style="text-align:left;padding:1px 2px">Q</th><th style="text-align:left;padding:1px 2px">Item</th><th style="text-align:right;padding:1px 2px">Action</th></tr>';
|
||||
body += '<tr style="color:#666"><th style="text-align:left;padding:1px 2px">Q</th>' +
|
||||
'<th style="text-align:left;padding:1px 2px">Item</th>' +
|
||||
'<th style="text-align:center;padding:1px 2px">Lv</th>' +
|
||||
'<th style="text-align:right;padding:1px 2px">Action</th></tr>';
|
||||
|
||||
for (const item of items) {
|
||||
const sameSlot = items.filter(i => i.slot === item.slot);
|
||||
const isBest = item === bestWeapon || item === bestArmor;
|
||||
let action;
|
||||
if (isBest) action = '✅';
|
||||
else if (item.quality === 'Crude' && sameSlot.length > 1) action = '💰';
|
||||
else action = '📦';
|
||||
let lastSlot = '';
|
||||
for (const item of graded) {
|
||||
// Slot separator
|
||||
if (item.slot && item.slot !== lastSlot) {
|
||||
lastSlot = item.slot;
|
||||
body += `<tr><td colspan="4" style="padding:4px 2px 1px;color:#555;font-size:8px;border-bottom:1px solid #333">${item.slot}</td></tr>`;
|
||||
}
|
||||
|
||||
const statusIcon = item.equipped ? '🗡' : item.locked ? '🔒' : item.stored ? '📦' : item.pinned ? '📌' : '';
|
||||
const nameShort = item.name.length > 28 ? item.name.slice(0, 27) + '…' : item.name;
|
||||
body += `<tr><td style="padding:1px 2px;color:${qColor(item.quality)}">${item.quality[0]}</td>
|
||||
<td style="padding:1px 2px">${item.name.slice(0, 30)}</td>
|
||||
<td style="padding:1px 2px;text-align:right">${action}</td></tr>`;
|
||||
<td style="padding:1px 2px">${statusIcon} ${nameShort}</td>
|
||||
<td style="padding:1px 2px;text-align:center;color:#888">${item.level || '?'}</td>
|
||||
<td style="padding:1px 2px;text-align:right;color:${item.grade.color}">${item.grade.label}</td></tr>`;
|
||||
}
|
||||
body += '</table>';
|
||||
body += '<div style="border-top:1px solid #374151;margin:6px 0;padding:4px 0;font-size:9px;color:#888">Go to Character → click empty slot → select item to equip</div>';
|
||||
|
||||
body += '<div style="border-top:1px solid #374151;margin:6px 0;padding:4px 0;font-size:9px;color:#555">' +
|
||||
'Status: 🗡Equipped 📌Pinned 🔒Locked 📦Stored</div>';
|
||||
|
||||
// ── Build panel container ──
|
||||
const collapsed = localStorage[SP + 'armoryCollapsed'] === '1';
|
||||
const panel = document.createElement('div');
|
||||
panel.id = 'hv-armory';
|
||||
panel.id = 'hv-armory-panel';
|
||||
panel.style.cssText = css({
|
||||
position: 'fixed',
|
||||
top: '60px',
|
||||
|
|
@ -2725,13 +2779,15 @@ function enhanceArmory() {
|
|||
borderRadius: '8px',
|
||||
fontSize: '10px',
|
||||
fontFamily: 'monospace',
|
||||
maxWidth: '320px',
|
||||
maxWidth: '350px',
|
||||
maxHeight: '80vh',
|
||||
overflowY: 'auto',
|
||||
boxShadow: '0 0 15px rgba(0,0,0,0.6)',
|
||||
border: '1px solid #374151',
|
||||
});
|
||||
|
||||
panel.innerHTML = `<div style="display:flex;justify-content:space-between;align-items:center;padding:6px 8px;cursor:pointer" id="hv-armory-header">
|
||||
<b style="color:#fdcb00;font-size:10px">🛡️ Equipment (${items.length})</b>
|
||||
<b style="color:#fdcb00;font-size:10px">🛡️ Armory (${items.length})</b>
|
||||
<span id="hv-armory-toggle" style="color:#888;font-size:12px">${collapsed ? '▶' : '▼'}</span>
|
||||
</div>
|
||||
<div id="hv-armory-body" style="padding:0 8px 6px;display:${collapsed ? 'none' : 'block'}">${body}</div>`;
|
||||
|
|
@ -2746,6 +2802,13 @@ function enhanceArmory() {
|
|||
};
|
||||
|
||||
document.body.appendChild(panel);
|
||||
|
||||
// ── Wire bulk buttons ──
|
||||
const sellBulk = document.getElementById('hv-armory-bulk-sell');
|
||||
const salvBulk = document.getElementById('hv-armory-bulk-salvage');
|
||||
|
||||
if (sellBulk) sellBulk.onclick = bulkSell;
|
||||
if (salvBulk) salvBulk.onclick = bulkSalvage;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
|
@ -2960,7 +3023,7 @@ function init() {
|
|||
} else {
|
||||
// Non-battle page enhancements
|
||||
if (STATE.page === 'itemshop') { enhanceItemShop(); autoCheckTask('buy-health'); }
|
||||
if (STATE.page === 'equipshop') { enhanceEquipShop(); enhanceEquipShopWithAdvice(); }
|
||||
if (STATE.page === 'equipshop') { enhanceEquipShopWithAdvice(); }
|
||||
if (STATE.page === 'shrine') enhanceShrine();
|
||||
if (STATE.page === 'training') { enhanceTraining(); autoCheckTask('training'); }
|
||||
if (STATE.page === 'monsterlab') { enhanceMonsterLab(); autoCheckTask('feed'); }
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// ==UserScript==
|
||||
// @name HV Unified
|
||||
// @namespace hvunified
|
||||
// @version 0.12.0
|
||||
// @version 0.13.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.12.0';
|
||||
const VERSION = '0.13.0';
|
||||
|
||||
const CFG = {
|
||||
// — Battle automation
|
||||
|
|
@ -1769,54 +1769,6 @@ function enhanceItemShop() {
|
|||
});
|
||||
}
|
||||
|
||||
// ── Equip Shop: quick sell/salvage buttons ──
|
||||
|
||||
function enhanceEquipShop() {
|
||||
if (!CFG.autoSell) return;
|
||||
const m = document.getElementById('mainpane');
|
||||
if (!m || document.getElementById('hv-quick-sell')) return;
|
||||
|
||||
const r = document.createElement('div');
|
||||
r.id = 'hv-quick-sell';
|
||||
r.style.cssText = 'margin:6px;display:flex;gap:6px;flex-wrap:wrap';
|
||||
|
||||
['Crude', 'Fair', 'Average', 'Superior'].forEach(q => {
|
||||
const b = document.createElement('input');
|
||||
b.type = 'button';
|
||||
b.value = `🔻 Sell ≤${q}`;
|
||||
b.style.cssText = 'padding:3px 8px;cursor:pointer;background:#d50c2d;color:#fff;border:none;border-radius:3px;font-size:11px';
|
||||
b.onclick = () => {
|
||||
$$('tr', m).forEach(row => {
|
||||
const t = (row.textContent || '');
|
||||
const qi = ['Crude', 'Fair', 'Average', 'Superior'].indexOf(q);
|
||||
if (['Crude', 'Fair', 'Average', 'Superior'].some((ql, i) => i <= qi && t.includes(ql)) && t.includes('Sell')) {
|
||||
const sb = row.querySelector('input[value="Sell"]');
|
||||
if (sb) sb.click();
|
||||
}
|
||||
});
|
||||
};
|
||||
r.appendChild(b);
|
||||
});
|
||||
|
||||
const s = document.createElement('input');
|
||||
s.type = 'button';
|
||||
s.value = '♻ Salvage ≤Average';
|
||||
s.style.cssText = 'padding:3px 8px;cursor:pointer;background:#555;color:#fff;border:none;border-radius:3px;font-size:11px';
|
||||
s.onclick = () => {
|
||||
$$('tr', m).forEach(row => {
|
||||
const t = (row.textContent || '');
|
||||
if ((t.includes('Crude') || t.includes('Fair') || t.includes('Average')) && t.includes('Salvage')) {
|
||||
const sb = row.querySelector('input[value="Salvage"]');
|
||||
if (sb) sb.click();
|
||||
}
|
||||
});
|
||||
};
|
||||
r.appendChild(s);
|
||||
|
||||
const ta = m.querySelector('div');
|
||||
if (ta) ta.insertBefore(r, ta.firstChild);
|
||||
}
|
||||
|
||||
// ── Equip advice: KEEP/SELL tags ──
|
||||
|
||||
function evaluateEquipment(text) {
|
||||
|
|
@ -2654,66 +2606,168 @@ function enhanceAbilities() {
|
|||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// ARMORY — equipment management panel
|
||||
// ARMORY — smart equipment inventory management
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
// Analyse equipment quality and assign a grade
|
||||
function gradeEquipment(name) {
|
||||
const q = KB.qualities.find(q => name.includes(q));
|
||||
if (!q) return { action: 'keep', label: '❓', color: '#888', reason: 'Unknown' };
|
||||
const qi = KB.qualities.indexOf(q);
|
||||
// Magnificent+ (index >= 5): always keep
|
||||
if (qi >= 5) return { action: 'keep', label: '✅ Keep', color: '#0f0', reason: `${q}+` };
|
||||
// Superior (index 3): borderline — keep if it's a weapon/armor we need
|
||||
if (qi === 3) return { action: 'keep', label: '⬆ Keep', color: '#fdcb00', reason: 'Superior' };
|
||||
// Average/Fair (1-2): salvage for materials
|
||||
if (qi >= 1) return { action: 'salvage', label: '♻ Salvage', color: '#888', reason: q };
|
||||
// Crude (0): sell
|
||||
return { action: 'sell', label: '💰 Sell', color: '#f80', reason: 'Crude' };
|
||||
}
|
||||
|
||||
function enhanceArmory() {
|
||||
const main = document.getElementById('mainpane');
|
||||
if (!main || document.getElementById('hv-armory')) return;
|
||||
if (!main || document.getElementById('hv-armory-panel')) return;
|
||||
|
||||
// ── Parse equipment from the page ──
|
||||
const items = [];
|
||||
let currentCategory = '';
|
||||
|
||||
$$('tr', main).forEach(row => {
|
||||
// Category label rows (Weapon, Armor sections)
|
||||
if (row.className === 'eqtplabel') {
|
||||
currentCategory = (row.textContent || '').trim();
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the equip item link/ID
|
||||
const links = row.querySelectorAll('a[href*="set_equip"]');
|
||||
if (links.length === 0) return;
|
||||
|
||||
// Item has an onmouseover for tooltip
|
||||
const omo = row.getAttribute('onmouseover') || '';
|
||||
const idMatch = omo.match(/hover_equip\((\d+)\)/);
|
||||
if (!idMatch) return;
|
||||
|
||||
const id = idMatch[1];
|
||||
const label = row.querySelector('label');
|
||||
if (!label) return;
|
||||
const name = (label.textContent || '').trim();
|
||||
const name = label ? (label.textContent || '').trim() : '';
|
||||
|
||||
// Detect level — HV renders Lv with CSS font in a .btm1 inside the row
|
||||
const levelDiv = row.querySelector('.btm1 label, .btm1');
|
||||
const levelText = levelDiv ? readCSSText(levelDiv) : '';
|
||||
const lvMatch = levelText.match(/lv\.?\s*(\d+)/i);
|
||||
const itemLevel = lvMatch ? parseInt(lvMatch[1]) : 0;
|
||||
|
||||
// Detect status indicators
|
||||
const rowHtml = row.innerHTML || '';
|
||||
const isLocked = rowHtml.includes('lock.png') || rowHtml.includes('lck.png');
|
||||
const isEquipped = rowHtml.includes('eqp.png') || rowHtml.includes('Equipped');
|
||||
const isPinned = rowHtml.includes('pin.png');
|
||||
const isStored = rowHtml.includes('store.png');
|
||||
|
||||
// Check for Sell/Salvage buttons in this row
|
||||
const sellBtn = row.querySelector('input[value="Sell"], a[onclick*="sell_equip"]');
|
||||
const salvBtn = row.querySelector('input[value="Salvage"], a[onclick*="salvage_equip"]');
|
||||
|
||||
const quality = KB.qualities.find(q => name.includes(q)) || '?';
|
||||
items.push({ id: idMatch[1], name, quality, slot: currentCategory });
|
||||
items.push({
|
||||
id, name, quality, slot: currentCategory,
|
||||
level: itemLevel,
|
||||
locked: isLocked, equipped: isEquipped,
|
||||
pinned: isPinned, stored: isStored,
|
||||
canSell: !!sellBtn, canSalvage: !!salvBtn,
|
||||
row,
|
||||
});
|
||||
});
|
||||
|
||||
if (items.length === 0) return;
|
||||
|
||||
// ── Grade each item ──
|
||||
const graded = items.map(item => ({
|
||||
...item,
|
||||
grade: gradeEquipment(item.name, item.slot),
|
||||
}));
|
||||
|
||||
// ── Sorting: equipped/pinned first, then by quality descending, then by level ──
|
||||
const qwords = KB.qualities;
|
||||
const best = (arr) => arr.sort((a, b) => qwords.indexOf(b.quality) - qwords.indexOf(a.quality))[0];
|
||||
const weapons = items.filter(i => !i.slot.includes('Armor') && !i.slot.includes('Shield'));
|
||||
const armors = items.filter(i => i.slot.includes('Armor') || i.slot.includes('Shoes'));
|
||||
const bestWeapon = best(weapons);
|
||||
const bestArmor = best(armors);
|
||||
const qColor = (q) => { const i = qwords.indexOf(q); return i >= 4 ? '#0f0' : i >= 2 ? '#fdcb00' : '#f80'; };
|
||||
graded.sort((a, b) => {
|
||||
if (a.equipped && !b.equipped) return -1;
|
||||
if (!a.equipped && b.equipped) return 1;
|
||||
if (a.pinned && !b.pinned) return -1;
|
||||
if (!a.pinned && b.pinned) return 1;
|
||||
const qDiff = (qwords.indexOf(b.quality) - qwords.indexOf(a.quality));
|
||||
if (qDiff !== 0) return qDiff;
|
||||
return b.level - a.level;
|
||||
});
|
||||
|
||||
let body = '';
|
||||
if (bestWeapon) body += `<div><span style="color:${qColor(bestWeapon.quality)}">${bestWeapon.quality}</span> <b>${bestWeapon.name}</b> ← best</div>`;
|
||||
if (bestArmor) body += `<div><span style="color:${qColor(bestArmor.quality)}">${bestArmor.quality}</span> <b>${bestArmor.name}</b> ← best</div>`;
|
||||
// ── Summary counts ──
|
||||
const keepCount = graded.filter(i => i.grade.action === 'keep').length;
|
||||
const sellCount = graded.filter(i => i.grade.action === 'sell').length;
|
||||
const salvCount = graded.filter(i => i.grade.action === 'salvage').length;
|
||||
const equippedCount = graded.filter(i => i.equipped).length;
|
||||
|
||||
body += '<div style="border-top:1px solid #374151;margin:6px 0"></div>';
|
||||
// ── Build bulk action buttons ──
|
||||
function bulkSell() {
|
||||
const toSell = graded.filter(i => i.grade.action === 'sell' && i.canSell && !i.equipped && !i.locked);
|
||||
if (toSell.length === 0) return;
|
||||
toSell.forEach(item => {
|
||||
const btn = item.row.querySelector('input[value="Sell"], a[onclick*="sell_equip"]');
|
||||
if (btn) btn.click();
|
||||
});
|
||||
}
|
||||
|
||||
function bulkSalvage() {
|
||||
const toSalv = graded.filter(i => i.grade.action === 'salvage' && i.canSalvage && !i.equipped && !i.locked);
|
||||
if (toSalv.length === 0) return;
|
||||
toSalv.forEach(item => {
|
||||
const btn = item.row.querySelector('input[value="Salvage"], a[onclick*="salvage_equip"]');
|
||||
if (btn) btn.click();
|
||||
});
|
||||
}
|
||||
|
||||
// ── Build the panel ──
|
||||
const qColor = (q) => { const i = qwords.indexOf(q); return i >= 5 ? '#0f0' : i >= 3 ? '#fdcb00' : i >= 1 ? '#888' : '#f80'; };
|
||||
|
||||
let body = `<div style="display:flex;gap:4px;margin-bottom:6px">
|
||||
<input type="button" value="💰 Sell Crude (${sellCount})" style="padding:3px 8px;cursor:pointer;background:#d50c2d;color:#fff;border:none;border-radius:3px;font-size:10px" id="hv-armory-bulk-sell">
|
||||
<input type="button" value="♻ Salvage ≤Fair (${salvCount})" style="padding:3px 8px;cursor:pointer;background:#555;color:#fff;border:none;border-radius:3px;font-size:10px" id="hv-armory-bulk-salvage">
|
||||
</div>`;
|
||||
|
||||
body += `<div style="border-top:1px solid #374151;margin:4px 0;padding:4px 0;font-size:9px;color:#888">
|
||||
✅ Keep: ${keepCount} | ♻ Salvage: ${salvCount} | 💰 Sell: ${sellCount} | 🗡 Equipped: ${equippedCount}
|
||||
</div>`;
|
||||
|
||||
// ── Table ──
|
||||
body += '<table style="width:100%;font-size:9px;border-collapse:collapse">';
|
||||
body += '<tr style="color:#888"><th style="text-align:left;padding:1px 2px">Q</th><th style="text-align:left;padding:1px 2px">Item</th><th style="text-align:right;padding:1px 2px">Action</th></tr>';
|
||||
body += '<tr style="color:#666"><th style="text-align:left;padding:1px 2px">Q</th>' +
|
||||
'<th style="text-align:left;padding:1px 2px">Item</th>' +
|
||||
'<th style="text-align:center;padding:1px 2px">Lv</th>' +
|
||||
'<th style="text-align:right;padding:1px 2px">Action</th></tr>';
|
||||
|
||||
for (const item of items) {
|
||||
const sameSlot = items.filter(i => i.slot === item.slot);
|
||||
const isBest = item === bestWeapon || item === bestArmor;
|
||||
let action;
|
||||
if (isBest) action = '✅';
|
||||
else if (item.quality === 'Crude' && sameSlot.length > 1) action = '💰';
|
||||
else action = '📦';
|
||||
let lastSlot = '';
|
||||
for (const item of graded) {
|
||||
// Slot separator
|
||||
if (item.slot && item.slot !== lastSlot) {
|
||||
lastSlot = item.slot;
|
||||
body += `<tr><td colspan="4" style="padding:4px 2px 1px;color:#555;font-size:8px;border-bottom:1px solid #333">${item.slot}</td></tr>`;
|
||||
}
|
||||
|
||||
const statusIcon = item.equipped ? '🗡' : item.locked ? '🔒' : item.stored ? '📦' : item.pinned ? '📌' : '';
|
||||
const nameShort = item.name.length > 28 ? item.name.slice(0, 27) + '…' : item.name;
|
||||
body += `<tr><td style="padding:1px 2px;color:${qColor(item.quality)}">${item.quality[0]}</td>
|
||||
<td style="padding:1px 2px">${item.name.slice(0, 30)}</td>
|
||||
<td style="padding:1px 2px;text-align:right">${action}</td></tr>`;
|
||||
<td style="padding:1px 2px">${statusIcon} ${nameShort}</td>
|
||||
<td style="padding:1px 2px;text-align:center;color:#888">${item.level || '?'}</td>
|
||||
<td style="padding:1px 2px;text-align:right;color:${item.grade.color}">${item.grade.label}</td></tr>`;
|
||||
}
|
||||
body += '</table>';
|
||||
body += '<div style="border-top:1px solid #374151;margin:6px 0;padding:4px 0;font-size:9px;color:#888">Go to Character → click empty slot → select item to equip</div>';
|
||||
|
||||
body += '<div style="border-top:1px solid #374151;margin:6px 0;padding:4px 0;font-size:9px;color:#555">' +
|
||||
'Status: 🗡Equipped 📌Pinned 🔒Locked 📦Stored</div>';
|
||||
|
||||
// ── Build panel container ──
|
||||
const collapsed = localStorage[SP + 'armoryCollapsed'] === '1';
|
||||
const panel = document.createElement('div');
|
||||
panel.id = 'hv-armory';
|
||||
panel.id = 'hv-armory-panel';
|
||||
panel.style.cssText = css({
|
||||
position: 'fixed',
|
||||
top: '60px',
|
||||
|
|
@ -2725,13 +2779,15 @@ function enhanceArmory() {
|
|||
borderRadius: '8px',
|
||||
fontSize: '10px',
|
||||
fontFamily: 'monospace',
|
||||
maxWidth: '320px',
|
||||
maxWidth: '350px',
|
||||
maxHeight: '80vh',
|
||||
overflowY: 'auto',
|
||||
boxShadow: '0 0 15px rgba(0,0,0,0.6)',
|
||||
border: '1px solid #374151',
|
||||
});
|
||||
|
||||
panel.innerHTML = `<div style="display:flex;justify-content:space-between;align-items:center;padding:6px 8px;cursor:pointer" id="hv-armory-header">
|
||||
<b style="color:#fdcb00;font-size:10px">🛡️ Equipment (${items.length})</b>
|
||||
<b style="color:#fdcb00;font-size:10px">🛡️ Armory (${items.length})</b>
|
||||
<span id="hv-armory-toggle" style="color:#888;font-size:12px">${collapsed ? '▶' : '▼'}</span>
|
||||
</div>
|
||||
<div id="hv-armory-body" style="padding:0 8px 6px;display:${collapsed ? 'none' : 'block'}">${body}</div>`;
|
||||
|
|
@ -2746,6 +2802,13 @@ function enhanceArmory() {
|
|||
};
|
||||
|
||||
document.body.appendChild(panel);
|
||||
|
||||
// ── Wire bulk buttons ──
|
||||
const sellBulk = document.getElementById('hv-armory-bulk-sell');
|
||||
const salvBulk = document.getElementById('hv-armory-bulk-salvage');
|
||||
|
||||
if (sellBulk) sellBulk.onclick = bulkSell;
|
||||
if (salvBulk) salvBulk.onclick = bulkSalvage;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
|
@ -2960,7 +3023,7 @@ function init() {
|
|||
} else {
|
||||
// Non-battle page enhancements
|
||||
if (STATE.page === 'itemshop') { enhanceItemShop(); autoCheckTask('buy-health'); }
|
||||
if (STATE.page === 'equipshop') { enhanceEquipShop(); enhanceEquipShopWithAdvice(); }
|
||||
if (STATE.page === 'equipshop') { enhanceEquipShopWithAdvice(); }
|
||||
if (STATE.page === 'shrine') enhanceShrine();
|
||||
if (STATE.page === 'training') { enhanceTraining(); autoCheckTask('training'); }
|
||||
if (STATE.page === 'monsterlab') { enhanceMonsterLab(); autoCheckTask('feed'); }
|
||||
|
|
|
|||
169
src/armory.js
169
src/armory.js
|
|
@ -1,64 +1,166 @@
|
|||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// ARMORY — equipment management panel
|
||||
// ARMORY — smart equipment inventory management
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
// Analyse equipment quality and assign a grade
|
||||
function gradeEquipment(name) {
|
||||
const q = KB.qualities.find(q => name.includes(q));
|
||||
if (!q) return { action: 'keep', label: '❓', color: '#888', reason: 'Unknown' };
|
||||
const qi = KB.qualities.indexOf(q);
|
||||
// Magnificent+ (index >= 5): always keep
|
||||
if (qi >= 5) return { action: 'keep', label: '✅ Keep', color: '#0f0', reason: `${q}+` };
|
||||
// Superior (index 3): borderline — keep if it's a weapon/armor we need
|
||||
if (qi === 3) return { action: 'keep', label: '⬆ Keep', color: '#fdcb00', reason: 'Superior' };
|
||||
// Average/Fair (1-2): salvage for materials
|
||||
if (qi >= 1) return { action: 'salvage', label: '♻ Salvage', color: '#888', reason: q };
|
||||
// Crude (0): sell
|
||||
return { action: 'sell', label: '💰 Sell', color: '#f80', reason: 'Crude' };
|
||||
}
|
||||
|
||||
function enhanceArmory() {
|
||||
const main = document.getElementById('mainpane');
|
||||
if (!main || document.getElementById('hv-armory')) return;
|
||||
if (!main || document.getElementById('hv-armory-panel')) return;
|
||||
|
||||
// ── Parse equipment from the page ──
|
||||
const items = [];
|
||||
let currentCategory = '';
|
||||
|
||||
$$('tr', main).forEach(row => {
|
||||
// Category label rows (Weapon, Armor sections)
|
||||
if (row.className === 'eqtplabel') {
|
||||
currentCategory = (row.textContent || '').trim();
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the equip item link/ID
|
||||
const links = row.querySelectorAll('a[href*="set_equip"]');
|
||||
if (links.length === 0) return;
|
||||
|
||||
// Item has an onmouseover for tooltip
|
||||
const omo = row.getAttribute('onmouseover') || '';
|
||||
const idMatch = omo.match(/hover_equip\((\d+)\)/);
|
||||
if (!idMatch) return;
|
||||
|
||||
const id = idMatch[1];
|
||||
const label = row.querySelector('label');
|
||||
if (!label) return;
|
||||
const name = (label.textContent || '').trim();
|
||||
const name = label ? (label.textContent || '').trim() : '';
|
||||
|
||||
// Detect level — HV renders Lv with CSS font in a .btm1 inside the row
|
||||
const levelDiv = row.querySelector('.btm1 label, .btm1');
|
||||
const levelText = levelDiv ? readCSSText(levelDiv) : '';
|
||||
const lvMatch = levelText.match(/lv\.?\s*(\d+)/i);
|
||||
const itemLevel = lvMatch ? parseInt(lvMatch[1]) : 0;
|
||||
|
||||
// Detect status indicators
|
||||
const rowHtml = row.innerHTML || '';
|
||||
const isLocked = rowHtml.includes('lock.png') || rowHtml.includes('lck.png');
|
||||
const isEquipped = rowHtml.includes('eqp.png') || rowHtml.includes('Equipped');
|
||||
const isPinned = rowHtml.includes('pin.png');
|
||||
const isStored = rowHtml.includes('store.png');
|
||||
|
||||
// Check for Sell/Salvage buttons in this row
|
||||
const sellBtn = row.querySelector('input[value="Sell"], a[onclick*="sell_equip"]');
|
||||
const salvBtn = row.querySelector('input[value="Salvage"], a[onclick*="salvage_equip"]');
|
||||
|
||||
const quality = KB.qualities.find(q => name.includes(q)) || '?';
|
||||
items.push({ id: idMatch[1], name, quality, slot: currentCategory });
|
||||
items.push({
|
||||
id, name, quality, slot: currentCategory,
|
||||
level: itemLevel,
|
||||
locked: isLocked, equipped: isEquipped,
|
||||
pinned: isPinned, stored: isStored,
|
||||
canSell: !!sellBtn, canSalvage: !!salvBtn,
|
||||
row,
|
||||
});
|
||||
});
|
||||
|
||||
if (items.length === 0) return;
|
||||
|
||||
// ── Grade each item ──
|
||||
const graded = items.map(item => ({
|
||||
...item,
|
||||
grade: gradeEquipment(item.name, item.slot),
|
||||
}));
|
||||
|
||||
// ── Sorting: equipped/pinned first, then by quality descending, then by level ──
|
||||
const qwords = KB.qualities;
|
||||
const best = (arr) => arr.sort((a, b) => qwords.indexOf(b.quality) - qwords.indexOf(a.quality))[0];
|
||||
const weapons = items.filter(i => !i.slot.includes('Armor') && !i.slot.includes('Shield'));
|
||||
const armors = items.filter(i => i.slot.includes('Armor') || i.slot.includes('Shoes'));
|
||||
const bestWeapon = best(weapons);
|
||||
const bestArmor = best(armors);
|
||||
const qColor = (q) => { const i = qwords.indexOf(q); return i >= 4 ? '#0f0' : i >= 2 ? '#fdcb00' : '#f80'; };
|
||||
graded.sort((a, b) => {
|
||||
if (a.equipped && !b.equipped) return -1;
|
||||
if (!a.equipped && b.equipped) return 1;
|
||||
if (a.pinned && !b.pinned) return -1;
|
||||
if (!a.pinned && b.pinned) return 1;
|
||||
const qDiff = (qwords.indexOf(b.quality) - qwords.indexOf(a.quality));
|
||||
if (qDiff !== 0) return qDiff;
|
||||
return b.level - a.level;
|
||||
});
|
||||
|
||||
let body = '';
|
||||
if (bestWeapon) body += `<div><span style="color:${qColor(bestWeapon.quality)}">${bestWeapon.quality}</span> <b>${bestWeapon.name}</b> ← best</div>`;
|
||||
if (bestArmor) body += `<div><span style="color:${qColor(bestArmor.quality)}">${bestArmor.quality}</span> <b>${bestArmor.name}</b> ← best</div>`;
|
||||
// ── Summary counts ──
|
||||
const keepCount = graded.filter(i => i.grade.action === 'keep').length;
|
||||
const sellCount = graded.filter(i => i.grade.action === 'sell').length;
|
||||
const salvCount = graded.filter(i => i.grade.action === 'salvage').length;
|
||||
const equippedCount = graded.filter(i => i.equipped).length;
|
||||
|
||||
body += '<div style="border-top:1px solid #374151;margin:6px 0"></div>';
|
||||
// ── Build bulk action buttons ──
|
||||
function bulkSell() {
|
||||
const toSell = graded.filter(i => i.grade.action === 'sell' && i.canSell && !i.equipped && !i.locked);
|
||||
if (toSell.length === 0) return;
|
||||
toSell.forEach(item => {
|
||||
const btn = item.row.querySelector('input[value="Sell"], a[onclick*="sell_equip"]');
|
||||
if (btn) btn.click();
|
||||
});
|
||||
}
|
||||
|
||||
function bulkSalvage() {
|
||||
const toSalv = graded.filter(i => i.grade.action === 'salvage' && i.canSalvage && !i.equipped && !i.locked);
|
||||
if (toSalv.length === 0) return;
|
||||
toSalv.forEach(item => {
|
||||
const btn = item.row.querySelector('input[value="Salvage"], a[onclick*="salvage_equip"]');
|
||||
if (btn) btn.click();
|
||||
});
|
||||
}
|
||||
|
||||
// ── Build the panel ──
|
||||
const qColor = (q) => { const i = qwords.indexOf(q); return i >= 5 ? '#0f0' : i >= 3 ? '#fdcb00' : i >= 1 ? '#888' : '#f80'; };
|
||||
|
||||
let body = `<div style="display:flex;gap:4px;margin-bottom:6px">
|
||||
<input type="button" value="💰 Sell Crude (${sellCount})" style="padding:3px 8px;cursor:pointer;background:#d50c2d;color:#fff;border:none;border-radius:3px;font-size:10px" id="hv-armory-bulk-sell">
|
||||
<input type="button" value="♻ Salvage ≤Fair (${salvCount})" style="padding:3px 8px;cursor:pointer;background:#555;color:#fff;border:none;border-radius:3px;font-size:10px" id="hv-armory-bulk-salvage">
|
||||
</div>`;
|
||||
|
||||
body += `<div style="border-top:1px solid #374151;margin:4px 0;padding:4px 0;font-size:9px;color:#888">
|
||||
✅ Keep: ${keepCount} | ♻ Salvage: ${salvCount} | 💰 Sell: ${sellCount} | 🗡 Equipped: ${equippedCount}
|
||||
</div>`;
|
||||
|
||||
// ── Table ──
|
||||
body += '<table style="width:100%;font-size:9px;border-collapse:collapse">';
|
||||
body += '<tr style="color:#888"><th style="text-align:left;padding:1px 2px">Q</th><th style="text-align:left;padding:1px 2px">Item</th><th style="text-align:right;padding:1px 2px">Action</th></tr>';
|
||||
body += '<tr style="color:#666"><th style="text-align:left;padding:1px 2px">Q</th>' +
|
||||
'<th style="text-align:left;padding:1px 2px">Item</th>' +
|
||||
'<th style="text-align:center;padding:1px 2px">Lv</th>' +
|
||||
'<th style="text-align:right;padding:1px 2px">Action</th></tr>';
|
||||
|
||||
for (const item of items) {
|
||||
const sameSlot = items.filter(i => i.slot === item.slot);
|
||||
const isBest = item === bestWeapon || item === bestArmor;
|
||||
let action;
|
||||
if (isBest) action = '✅';
|
||||
else if (item.quality === 'Crude' && sameSlot.length > 1) action = '💰';
|
||||
else action = '📦';
|
||||
let lastSlot = '';
|
||||
for (const item of graded) {
|
||||
// Slot separator
|
||||
if (item.slot && item.slot !== lastSlot) {
|
||||
lastSlot = item.slot;
|
||||
body += `<tr><td colspan="4" style="padding:4px 2px 1px;color:#555;font-size:8px;border-bottom:1px solid #333">${item.slot}</td></tr>`;
|
||||
}
|
||||
|
||||
const statusIcon = item.equipped ? '🗡' : item.locked ? '🔒' : item.stored ? '📦' : item.pinned ? '📌' : '';
|
||||
const nameShort = item.name.length > 28 ? item.name.slice(0, 27) + '…' : item.name;
|
||||
body += `<tr><td style="padding:1px 2px;color:${qColor(item.quality)}">${item.quality[0]}</td>
|
||||
<td style="padding:1px 2px">${item.name.slice(0, 30)}</td>
|
||||
<td style="padding:1px 2px;text-align:right">${action}</td></tr>`;
|
||||
<td style="padding:1px 2px">${statusIcon} ${nameShort}</td>
|
||||
<td style="padding:1px 2px;text-align:center;color:#888">${item.level || '?'}</td>
|
||||
<td style="padding:1px 2px;text-align:right;color:${item.grade.color}">${item.grade.label}</td></tr>`;
|
||||
}
|
||||
body += '</table>';
|
||||
body += '<div style="border-top:1px solid #374151;margin:6px 0;padding:4px 0;font-size:9px;color:#888">Go to Character → click empty slot → select item to equip</div>';
|
||||
|
||||
body += '<div style="border-top:1px solid #374151;margin:6px 0;padding:4px 0;font-size:9px;color:#555">' +
|
||||
'Status: 🗡Equipped 📌Pinned 🔒Locked 📦Stored</div>';
|
||||
|
||||
// ── Build panel container ──
|
||||
const collapsed = localStorage[SP + 'armoryCollapsed'] === '1';
|
||||
const panel = document.createElement('div');
|
||||
panel.id = 'hv-armory';
|
||||
panel.id = 'hv-armory-panel';
|
||||
panel.style.cssText = css({
|
||||
position: 'fixed',
|
||||
top: '60px',
|
||||
|
|
@ -70,13 +172,15 @@ function enhanceArmory() {
|
|||
borderRadius: '8px',
|
||||
fontSize: '10px',
|
||||
fontFamily: 'monospace',
|
||||
maxWidth: '320px',
|
||||
maxWidth: '350px',
|
||||
maxHeight: '80vh',
|
||||
overflowY: 'auto',
|
||||
boxShadow: '0 0 15px rgba(0,0,0,0.6)',
|
||||
border: '1px solid #374151',
|
||||
});
|
||||
|
||||
panel.innerHTML = `<div style="display:flex;justify-content:space-between;align-items:center;padding:6px 8px;cursor:pointer" id="hv-armory-header">
|
||||
<b style="color:#fdcb00;font-size:10px">🛡️ Equipment (${items.length})</b>
|
||||
<b style="color:#fdcb00;font-size:10px">🛡️ Armory (${items.length})</b>
|
||||
<span id="hv-armory-toggle" style="color:#888;font-size:12px">${collapsed ? '▶' : '▼'}</span>
|
||||
</div>
|
||||
<div id="hv-armory-body" style="padding:0 8px 6px;display:${collapsed ? 'none' : 'block'}">${body}</div>`;
|
||||
|
|
@ -91,4 +195,11 @@ function enhanceArmory() {
|
|||
};
|
||||
|
||||
document.body.appendChild(panel);
|
||||
|
||||
// ── Wire bulk buttons ──
|
||||
const sellBulk = document.getElementById('hv-armory-bulk-sell');
|
||||
const salvBulk = document.getElementById('hv-armory-bulk-salvage');
|
||||
|
||||
if (sellBulk) sellBulk.onclick = bulkSell;
|
||||
if (salvBulk) salvBulk.onclick = bulkSalvage;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ function init() {
|
|||
} else {
|
||||
// Non-battle page enhancements
|
||||
if (STATE.page === 'itemshop') { enhanceItemShop(); autoCheckTask('buy-health'); }
|
||||
if (STATE.page === 'equipshop') { enhanceEquipShop(); enhanceEquipShopWithAdvice(); }
|
||||
if (STATE.page === 'equipshop') { enhanceEquipShopWithAdvice(); }
|
||||
if (STATE.page === 'shrine') enhanceShrine();
|
||||
if (STATE.page === 'training') { enhanceTraining(); autoCheckTask('training'); }
|
||||
if (STATE.page === 'monsterlab') { enhanceMonsterLab(); autoCheckTask('feed'); }
|
||||
|
|
|
|||
|
|
@ -90,54 +90,6 @@ function enhanceItemShop() {
|
|||
});
|
||||
}
|
||||
|
||||
// ── Equip Shop: quick sell/salvage buttons ──
|
||||
|
||||
function enhanceEquipShop() {
|
||||
if (!CFG.autoSell) return;
|
||||
const m = document.getElementById('mainpane');
|
||||
if (!m || document.getElementById('hv-quick-sell')) return;
|
||||
|
||||
const r = document.createElement('div');
|
||||
r.id = 'hv-quick-sell';
|
||||
r.style.cssText = 'margin:6px;display:flex;gap:6px;flex-wrap:wrap';
|
||||
|
||||
['Crude', 'Fair', 'Average', 'Superior'].forEach(q => {
|
||||
const b = document.createElement('input');
|
||||
b.type = 'button';
|
||||
b.value = `🔻 Sell ≤${q}`;
|
||||
b.style.cssText = 'padding:3px 8px;cursor:pointer;background:#d50c2d;color:#fff;border:none;border-radius:3px;font-size:11px';
|
||||
b.onclick = () => {
|
||||
$$('tr', m).forEach(row => {
|
||||
const t = (row.textContent || '');
|
||||
const qi = ['Crude', 'Fair', 'Average', 'Superior'].indexOf(q);
|
||||
if (['Crude', 'Fair', 'Average', 'Superior'].some((ql, i) => i <= qi && t.includes(ql)) && t.includes('Sell')) {
|
||||
const sb = row.querySelector('input[value="Sell"]');
|
||||
if (sb) sb.click();
|
||||
}
|
||||
});
|
||||
};
|
||||
r.appendChild(b);
|
||||
});
|
||||
|
||||
const s = document.createElement('input');
|
||||
s.type = 'button';
|
||||
s.value = '♻ Salvage ≤Average';
|
||||
s.style.cssText = 'padding:3px 8px;cursor:pointer;background:#555;color:#fff;border:none;border-radius:3px;font-size:11px';
|
||||
s.onclick = () => {
|
||||
$$('tr', m).forEach(row => {
|
||||
const t = (row.textContent || '');
|
||||
if ((t.includes('Crude') || t.includes('Fair') || t.includes('Average')) && t.includes('Salvage')) {
|
||||
const sb = row.querySelector('input[value="Salvage"]');
|
||||
if (sb) sb.click();
|
||||
}
|
||||
});
|
||||
};
|
||||
r.appendChild(s);
|
||||
|
||||
const ta = m.querySelector('div');
|
||||
if (ta) ta.insertBefore(r, ta.firstChild);
|
||||
}
|
||||
|
||||
// ── Equip advice: KEEP/SELL tags ──
|
||||
|
||||
function evaluateEquipment(text) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue