v0.15.3 - Scorer now accounts for weapon damage, elemental damage, and procs
Parser fix: parseEquipHTML now captures the direct-child divs of the
tooltip that were previously skipped:
- Proc lines: 'Penetrated Armor: 21.7% chance' -> stats['Proc Penetrated Armor']
- Base damage: '+708 Piercing Damage' -> stats['Weapon Damage']
- Elemental lines: '+25 Fire Damage' / '+30 Elec Strike' -> stats['Elemental Fire']
Scorer (gearScoreWeapon + analyze_gear.py):
- Weapon Damage x 0.30 (base damage matters)
- Elemental damage x 0.25 per element
- Proc bonuses by type: CC procs (stun/freeze) x0.45, armor break
x0.35, DoT x0.25, generic x0.15
- All multiplied by the same quality multiplier afterward
So an elemental weapon with a strike proc now gets its due — the
short-margin stat winner may no longer win once damage+proc count.
Note: re-scrape the armory/store after updating so the new fields
get captured (old entries lack Weapon Damage/Proc keys).
This commit is contained in:
parent
b069d42fc8
commit
f993014a46
7 changed files with 182 additions and 6 deletions
|
|
@ -67,6 +67,20 @@ def score_weapon(item):
|
||||||
s += get_num(stats.get('Block',0)) * 0.5
|
s += get_num(stats.get('Block',0)) * 0.5
|
||||||
s += get_num(stats.get('Agility',0)) * 0.5
|
s += get_num(stats.get('Agility',0)) * 0.5
|
||||||
s -= get_num(stats.get('burden',0)) * 0.5
|
s -= get_num(stats.get('burden',0)) * 0.5
|
||||||
|
# Base weapon damage + elemental damage
|
||||||
|
s += get_num(stats.get('Weapon Damage',0)) * 0.30
|
||||||
|
for el in ['Fire','Cold','Elec','Wind','Holy','Dark','Ethereal','Elemental']:
|
||||||
|
s += get_num(stats.get('Elemental ' + el,0)) * 0.25
|
||||||
|
# Proc lines
|
||||||
|
for k, v in stats.items():
|
||||||
|
if k.startswith('Proc '):
|
||||||
|
pct = get_num(v)
|
||||||
|
pname = k.replace('Proc ','').lower()
|
||||||
|
if 'penetrated armor' in pname or 'armor' in pname: s += pct * 0.35
|
||||||
|
elif any(x in pname for x in ['stun','freeze','slow','paraly']): s += pct * 0.45
|
||||||
|
elif any(x in pname for x in ['bleeding','wound','poison','burn','shock']): s += pct * 0.25
|
||||||
|
elif 'weaken' in pname or 'impair' in pname: s += pct * 0.25
|
||||||
|
else: s += pct * 0.15
|
||||||
|
|
||||||
# Quality multiplier: higher tiers have better base damage
|
# Quality multiplier: higher tiers have better base damage
|
||||||
# q6=Magnificent, q5=Exquisite, q4=Superior, q3=Average, q2=Fair, q1=Crude
|
# q6=Magnificent, q5=Exquisite, q4=Superior, q3=Average, q2=Fair, q1=Crude
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name HV Unified
|
// @name HV Unified
|
||||||
// @namespace hvunified
|
// @namespace hvunified
|
||||||
// @version 0.15.2
|
// @version 0.15.3
|
||||||
// @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.15.2';
|
const VERSION = '0.15.3';
|
||||||
|
|
||||||
const CFG = {
|
const CFG = {
|
||||||
// — Battle automation
|
// — Battle automation
|
||||||
|
|
@ -3512,6 +3512,41 @@ function parseEquipHTML(htmlStr) {
|
||||||
const ex = eq.querySelector('.ex');
|
const ex = eq.querySelector('.ex');
|
||||||
if (ex) ex.querySelectorAll(':scope > div').forEach(parseRow);
|
if (ex) ex.querySelectorAll(':scope > div').forEach(parseRow);
|
||||||
|
|
||||||
|
// Direct-child rows of .eq — weapon damage + proc lines.
|
||||||
|
// e.g. <div><span>Penetrated Armor</span>: <span>21.7% chance</span></div>
|
||||||
|
// <div title="Base: 134">+<span>708 Piercing Damage</span></div>
|
||||||
|
Array.from(eq.children).forEach(child => {
|
||||||
|
if (child.classList && (child.classList.contains('eqt') || child.classList.contains('eqr')
|
||||||
|
|| child.classList.contains('eqc') || child.classList.contains('ex') || child.classList.contains('ep'))) return;
|
||||||
|
const text = (child.textContent || '').trim();
|
||||||
|
if (!text) return;
|
||||||
|
|
||||||
|
// Proc line: "Name: 21.7% chance"
|
||||||
|
const procM = text.match(/^([A-Za-z][A-Za-z ]+):\s*([\d.]+%?\s*(?:chance|procs?)?)/);
|
||||||
|
if (procM && child.querySelector('span')) {
|
||||||
|
stats['Proc ' + procM[1].trim()] = procM[2].trim();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Weapon damage line: "+708 Piercing Damage"
|
||||||
|
const dmgM = text.match(/^\+([\d,]+)\s*([A-Za-z]+)\s*Damage$/);
|
||||||
|
if (dmgM) {
|
||||||
|
stats['Weapon Damage'] = parseInt(dmgM[1].replace(/,/g, ''));
|
||||||
|
stats['Damage Type'] = dmgM[2];
|
||||||
|
const title = child.getAttribute('title') || '';
|
||||||
|
const baseM = title.match(/Base:\s*(\d+)/i);
|
||||||
|
if (baseM) stats['Weapon Damage Base'] = parseInt(baseM[1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Elemental damage lines: "+25 Fire Damage" or "+30 Elec Damage"
|
||||||
|
const elemM = text.match(/^\+([\d,]+)\s*(Fire|Cold|Elec|Wind|Holy|Dark|Ethereal|Elemental)\s*(?:Damage|Strike)$/);
|
||||||
|
if (elemM) {
|
||||||
|
stats['Elemental ' + elemM[2]] = parseInt(elemM[1].replace(/,/g, ''));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Extra stat groups (.ep)
|
// Extra stat groups (.ep)
|
||||||
eq.querySelectorAll('.ep').forEach(g => {
|
eq.querySelectorAll('.ep').forEach(g => {
|
||||||
g.querySelectorAll(':scope > div:not(:first-child)').forEach(parseRow);
|
g.querySelectorAll(':scope > div:not(:first-child)').forEach(parseRow);
|
||||||
|
|
@ -3829,6 +3864,25 @@ function gearScoreWeapon(item) {
|
||||||
s += _num(st['Block']) * 0.5;
|
s += _num(st['Block']) * 0.5;
|
||||||
s += _num(st['Agility']) * 0.5;
|
s += _num(st['Agility']) * 0.5;
|
||||||
s -= _num(st['burden']) * 0.5;
|
s -= _num(st['burden']) * 0.5;
|
||||||
|
// Base weapon damage (e.g. "+708 Piercing Damage") — scales with quality
|
||||||
|
s += _num(st['Weapon Damage']) * 0.30;
|
||||||
|
// Elemental damage lines (e.g. "+25 Fire Damage", "+30 Elec Strike")
|
||||||
|
['Fire', 'Cold', 'Elec', 'Wind', 'Holy', 'Dark', 'Ethereal', 'Elemental'].forEach(el => {
|
||||||
|
s += _num(st['Elemental ' + el]) * 0.25;
|
||||||
|
});
|
||||||
|
// Proc lines: elemental/status procs add value (e.g. "Proc Penetrated Armor: 21.7%")
|
||||||
|
let procBonus = 0;
|
||||||
|
Object.keys(st).forEach(k => {
|
||||||
|
if (!k.startsWith('Proc ')) return;
|
||||||
|
const pct = _num(st[k]);
|
||||||
|
const pname = k.replace('Proc ', '').toLowerCase();
|
||||||
|
if (pname.includes('penetrated armor') || pname.includes('armor')) procBonus += pct * 0.35;
|
||||||
|
else if (pname.includes('stun') || pname.includes('freeze') || pname.includes('slow') || pname.includes('paraly')) procBonus += pct * 0.45;
|
||||||
|
else if (pname.includes('bleeding') || pname.includes('wound') || pname.includes('poison') || pname.includes('burn') || pname.includes('shock')) procBonus += pct * 0.25;
|
||||||
|
else if (pname.includes('weaken') || pname.includes('impair')) procBonus += pct * 0.25;
|
||||||
|
else procBonus += pct * 0.15; // generic proc value
|
||||||
|
});
|
||||||
|
s += procBonus;
|
||||||
// Quality multiplier (q6=Magnificent, q5=Exquisite, q4=Superior)
|
// Quality multiplier (q6=Magnificent, q5=Exquisite, q4=Superior)
|
||||||
s *= 0.8 + (quality * 0.12);
|
s *= 0.8 + (quality * 0.12);
|
||||||
// Weapon type bonus
|
// Weapon type bonus
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name HV Unified
|
// @name HV Unified
|
||||||
// @namespace hvunified
|
// @namespace hvunified
|
||||||
// @version 0.15.2
|
// @version 0.15.3
|
||||||
// @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.15.2';
|
const VERSION = '0.15.3';
|
||||||
|
|
||||||
const CFG = {
|
const CFG = {
|
||||||
// — Battle automation
|
// — Battle automation
|
||||||
|
|
@ -3512,6 +3512,41 @@ function parseEquipHTML(htmlStr) {
|
||||||
const ex = eq.querySelector('.ex');
|
const ex = eq.querySelector('.ex');
|
||||||
if (ex) ex.querySelectorAll(':scope > div').forEach(parseRow);
|
if (ex) ex.querySelectorAll(':scope > div').forEach(parseRow);
|
||||||
|
|
||||||
|
// Direct-child rows of .eq — weapon damage + proc lines.
|
||||||
|
// e.g. <div><span>Penetrated Armor</span>: <span>21.7% chance</span></div>
|
||||||
|
// <div title="Base: 134">+<span>708 Piercing Damage</span></div>
|
||||||
|
Array.from(eq.children).forEach(child => {
|
||||||
|
if (child.classList && (child.classList.contains('eqt') || child.classList.contains('eqr')
|
||||||
|
|| child.classList.contains('eqc') || child.classList.contains('ex') || child.classList.contains('ep'))) return;
|
||||||
|
const text = (child.textContent || '').trim();
|
||||||
|
if (!text) return;
|
||||||
|
|
||||||
|
// Proc line: "Name: 21.7% chance"
|
||||||
|
const procM = text.match(/^([A-Za-z][A-Za-z ]+):\s*([\d.]+%?\s*(?:chance|procs?)?)/);
|
||||||
|
if (procM && child.querySelector('span')) {
|
||||||
|
stats['Proc ' + procM[1].trim()] = procM[2].trim();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Weapon damage line: "+708 Piercing Damage"
|
||||||
|
const dmgM = text.match(/^\+([\d,]+)\s*([A-Za-z]+)\s*Damage$/);
|
||||||
|
if (dmgM) {
|
||||||
|
stats['Weapon Damage'] = parseInt(dmgM[1].replace(/,/g, ''));
|
||||||
|
stats['Damage Type'] = dmgM[2];
|
||||||
|
const title = child.getAttribute('title') || '';
|
||||||
|
const baseM = title.match(/Base:\s*(\d+)/i);
|
||||||
|
if (baseM) stats['Weapon Damage Base'] = parseInt(baseM[1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Elemental damage lines: "+25 Fire Damage" or "+30 Elec Damage"
|
||||||
|
const elemM = text.match(/^\+([\d,]+)\s*(Fire|Cold|Elec|Wind|Holy|Dark|Ethereal|Elemental)\s*(?:Damage|Strike)$/);
|
||||||
|
if (elemM) {
|
||||||
|
stats['Elemental ' + elemM[2]] = parseInt(elemM[1].replace(/,/g, ''));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Extra stat groups (.ep)
|
// Extra stat groups (.ep)
|
||||||
eq.querySelectorAll('.ep').forEach(g => {
|
eq.querySelectorAll('.ep').forEach(g => {
|
||||||
g.querySelectorAll(':scope > div:not(:first-child)').forEach(parseRow);
|
g.querySelectorAll(':scope > div:not(:first-child)').forEach(parseRow);
|
||||||
|
|
@ -3829,6 +3864,25 @@ function gearScoreWeapon(item) {
|
||||||
s += _num(st['Block']) * 0.5;
|
s += _num(st['Block']) * 0.5;
|
||||||
s += _num(st['Agility']) * 0.5;
|
s += _num(st['Agility']) * 0.5;
|
||||||
s -= _num(st['burden']) * 0.5;
|
s -= _num(st['burden']) * 0.5;
|
||||||
|
// Base weapon damage (e.g. "+708 Piercing Damage") — scales with quality
|
||||||
|
s += _num(st['Weapon Damage']) * 0.30;
|
||||||
|
// Elemental damage lines (e.g. "+25 Fire Damage", "+30 Elec Strike")
|
||||||
|
['Fire', 'Cold', 'Elec', 'Wind', 'Holy', 'Dark', 'Ethereal', 'Elemental'].forEach(el => {
|
||||||
|
s += _num(st['Elemental ' + el]) * 0.25;
|
||||||
|
});
|
||||||
|
// Proc lines: elemental/status procs add value (e.g. "Proc Penetrated Armor: 21.7%")
|
||||||
|
let procBonus = 0;
|
||||||
|
Object.keys(st).forEach(k => {
|
||||||
|
if (!k.startsWith('Proc ')) return;
|
||||||
|
const pct = _num(st[k]);
|
||||||
|
const pname = k.replace('Proc ', '').toLowerCase();
|
||||||
|
if (pname.includes('penetrated armor') || pname.includes('armor')) procBonus += pct * 0.35;
|
||||||
|
else if (pname.includes('stun') || pname.includes('freeze') || pname.includes('slow') || pname.includes('paraly')) procBonus += pct * 0.45;
|
||||||
|
else if (pname.includes('bleeding') || pname.includes('wound') || pname.includes('poison') || pname.includes('burn') || pname.includes('shock')) procBonus += pct * 0.25;
|
||||||
|
else if (pname.includes('weaken') || pname.includes('impair')) procBonus += pct * 0.25;
|
||||||
|
else procBonus += pct * 0.15; // generic proc value
|
||||||
|
});
|
||||||
|
s += procBonus;
|
||||||
// Quality multiplier (q6=Magnificent, q5=Exquisite, q4=Superior)
|
// Quality multiplier (q6=Magnificent, q5=Exquisite, q4=Superior)
|
||||||
s *= 0.8 + (quality * 0.12);
|
s *= 0.8 + (quality * 0.12);
|
||||||
// Weapon type bonus
|
// Weapon type bonus
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// CONFIG — default settings
|
// CONFIG — default settings
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
const VERSION = '0.15.2';
|
const VERSION = '0.15.3';
|
||||||
|
|
||||||
const CFG = {
|
const CFG = {
|
||||||
// — Battle automation
|
// — Battle automation
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,25 @@ function gearScoreWeapon(item) {
|
||||||
s += _num(st['Block']) * 0.5;
|
s += _num(st['Block']) * 0.5;
|
||||||
s += _num(st['Agility']) * 0.5;
|
s += _num(st['Agility']) * 0.5;
|
||||||
s -= _num(st['burden']) * 0.5;
|
s -= _num(st['burden']) * 0.5;
|
||||||
|
// Base weapon damage (e.g. "+708 Piercing Damage") — scales with quality
|
||||||
|
s += _num(st['Weapon Damage']) * 0.30;
|
||||||
|
// Elemental damage lines (e.g. "+25 Fire Damage", "+30 Elec Strike")
|
||||||
|
['Fire', 'Cold', 'Elec', 'Wind', 'Holy', 'Dark', 'Ethereal', 'Elemental'].forEach(el => {
|
||||||
|
s += _num(st['Elemental ' + el]) * 0.25;
|
||||||
|
});
|
||||||
|
// Proc lines: elemental/status procs add value (e.g. "Proc Penetrated Armor: 21.7%")
|
||||||
|
let procBonus = 0;
|
||||||
|
Object.keys(st).forEach(k => {
|
||||||
|
if (!k.startsWith('Proc ')) return;
|
||||||
|
const pct = _num(st[k]);
|
||||||
|
const pname = k.replace('Proc ', '').toLowerCase();
|
||||||
|
if (pname.includes('penetrated armor') || pname.includes('armor')) procBonus += pct * 0.35;
|
||||||
|
else if (pname.includes('stun') || pname.includes('freeze') || pname.includes('slow') || pname.includes('paraly')) procBonus += pct * 0.45;
|
||||||
|
else if (pname.includes('bleeding') || pname.includes('wound') || pname.includes('poison') || pname.includes('burn') || pname.includes('shock')) procBonus += pct * 0.25;
|
||||||
|
else if (pname.includes('weaken') || pname.includes('impair')) procBonus += pct * 0.25;
|
||||||
|
else procBonus += pct * 0.15; // generic proc value
|
||||||
|
});
|
||||||
|
s += procBonus;
|
||||||
// Quality multiplier (q6=Magnificent, q5=Exquisite, q4=Superior)
|
// Quality multiplier (q6=Magnificent, q5=Exquisite, q4=Superior)
|
||||||
s *= 0.8 + (quality * 0.12);
|
s *= 0.8 + (quality * 0.12);
|
||||||
// Weapon type bonus
|
// Weapon type bonus
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,41 @@ function parseEquipHTML(htmlStr) {
|
||||||
const ex = eq.querySelector('.ex');
|
const ex = eq.querySelector('.ex');
|
||||||
if (ex) ex.querySelectorAll(':scope > div').forEach(parseRow);
|
if (ex) ex.querySelectorAll(':scope > div').forEach(parseRow);
|
||||||
|
|
||||||
|
// Direct-child rows of .eq — weapon damage + proc lines.
|
||||||
|
// e.g. <div><span>Penetrated Armor</span>: <span>21.7% chance</span></div>
|
||||||
|
// <div title="Base: 134">+<span>708 Piercing Damage</span></div>
|
||||||
|
Array.from(eq.children).forEach(child => {
|
||||||
|
if (child.classList && (child.classList.contains('eqt') || child.classList.contains('eqr')
|
||||||
|
|| child.classList.contains('eqc') || child.classList.contains('ex') || child.classList.contains('ep'))) return;
|
||||||
|
const text = (child.textContent || '').trim();
|
||||||
|
if (!text) return;
|
||||||
|
|
||||||
|
// Proc line: "Name: 21.7% chance"
|
||||||
|
const procM = text.match(/^([A-Za-z][A-Za-z ]+):\s*([\d.]+%?\s*(?:chance|procs?)?)/);
|
||||||
|
if (procM && child.querySelector('span')) {
|
||||||
|
stats['Proc ' + procM[1].trim()] = procM[2].trim();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Weapon damage line: "+708 Piercing Damage"
|
||||||
|
const dmgM = text.match(/^\+([\d,]+)\s*([A-Za-z]+)\s*Damage$/);
|
||||||
|
if (dmgM) {
|
||||||
|
stats['Weapon Damage'] = parseInt(dmgM[1].replace(/,/g, ''));
|
||||||
|
stats['Damage Type'] = dmgM[2];
|
||||||
|
const title = child.getAttribute('title') || '';
|
||||||
|
const baseM = title.match(/Base:\s*(\d+)/i);
|
||||||
|
if (baseM) stats['Weapon Damage Base'] = parseInt(baseM[1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Elemental damage lines: "+25 Fire Damage" or "+30 Elec Damage"
|
||||||
|
const elemM = text.match(/^\+([\d,]+)\s*(Fire|Cold|Elec|Wind|Holy|Dark|Ethereal|Elemental)\s*(?:Damage|Strike)$/);
|
||||||
|
if (elemM) {
|
||||||
|
stats['Elemental ' + elemM[2]] = parseInt(elemM[1].replace(/,/g, ''));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Extra stat groups (.ep)
|
// Extra stat groups (.ep)
|
||||||
eq.querySelectorAll('.ep').forEach(g => {
|
eq.querySelectorAll('.ep').forEach(g => {
|
||||||
g.querySelectorAll(':scope > div:not(:first-child)').forEach(parseRow);
|
g.querySelectorAll(':scope > div:not(:first-child)').forEach(parseRow);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name HV Unified
|
// @name HV Unified
|
||||||
// @namespace hvunified
|
// @namespace hvunified
|
||||||
// @version 0.15.2
|
// @version 0.15.3
|
||||||
// @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