v0.15.4 - Elemental Strikes parsed + scored per wiki (Void Strike, etc.)

From EHWiki Detailed_Equip_Characteristics + Equipment_Basics:
- Elemental strikes are 'X Strike (Y%)' lines (Void/Fire/Cold/Elec/
  Wind/Holy/Dark), separate hit for ~50% of physical damage, max 2
  strikes + Void Strike
- Ethereal weapons deal Void damage (+523 Void Damage), prefixes:
  Ethereal→Void, Fiery→Fire, Arctic→Cold, Shocking→Elec,
  Tempestuous→Wind, Hallowed→Holy, Demonic→Dark
- Weapon procs by type: Piercing→Penetrated Armor (Estoc/Rapier),
  Crushing→Stun (Mace/Club), Slashing→Bleeding Wound (Longsword/
  Katana/Shortsword/Axe/Wakizashi)

Parser now captures:
- stats['Strike <Elem>'] = chance% (e.g. 'Strike Void': 50)
- stats['Weapon Damage'] for Void too (+523 Void Damage)

Scorer adds expected strike damage (chance/100 × 0.5 × weapon dmg)
weighted same as base damage. Type bonuses updated to match real
procs (Stun 1.15x > Bleeding 1.10x, Rapier 1.20x like Estoc 1.25x).

So your lottery Peerless Ethereal Rapier (+523 Void, Void Strike 50%)
now scores properly. Note: re-scrape after update for Strike fields.
This commit is contained in:
GaboGG 2026-08-03 16:04:37 -04:00
parent f993014a46
commit 589ed7e34c
7 changed files with 101 additions and 54 deletions

View file

@ -67,10 +67,14 @@ 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 # Base weapon damage + elemental strikes
s += get_num(stats.get('Weapon Damage',0)) * 0.30 wd = get_num(stats.get('Weapon Damage',0))
for el in ['Fire','Cold','Elec','Wind','Holy','Dark','Ethereal','Elemental']: s += wd * 0.30
s += get_num(stats.get('Elemental ' + el,0)) * 0.25 for el in ['Fire','Cold','Elec','Wind','Holy','Dark','Void','Ethereal']:
chance = get_num(stats.get('Strike ' + el,0))
if chance > 0:
expected = (chance / 100) * 0.5 * wd
s += expected * 0.30 if expected > 0 else chance * 0.15
# Proc lines # Proc lines
for k, v in stats.items(): for k, v in stats.items():
if k.startswith('Proc '): if k.startswith('Proc '):
@ -87,18 +91,16 @@ def score_weapon(item):
quality_mult = 0.8 + (quality * 0.12) # q4=1.28x, q5=1.40x, q6=1.52x quality_mult = 0.8 + (quality * 0.12) # q4=1.28x, q5=1.40x, q6=1.52x
s *= quality_mult s *= quality_mult
# Weapon type bonus: Estoc has Penetrated Armor proc (armor break) # Weapon type bonus (wiki procs: Piercing→Penetrated Armor, Crushing→Stun,
# Longsword has Weaken proc (damage debuff on enemies) # Slashing→Bleeding Wound; Estoc=2H Penetrated Armor is the 2H physical build's core)
if 'estoc' in name_lower: if 'estoc' in name_lower:
s *= 1.25 # Estoc's Penetrated Armor is the defining proc for 2H physical builds s *= 1.25
elif 'longsword' in name_lower: elif 'rapier' in name_lower:
s *= 1.10 # Weaken proc is decent but not as impactful s *= 1.20 # 1H Penetrated Armor
elif 'great mace' in name_lower: elif any(k in name_lower for k in ['longsword','katana','shortsword','axe','wakizashi']):
s *= 0.90 # No armor pen, lower utility s *= 1.10 # Bleeding Wound
elif 'club' in name_lower: elif any(k in name_lower for k in ['great mace','club','mace']):
s *= 0.85 s *= 1.15 # Stun
elif 'axe' in name_lower:
s *= 0.90
return s return s

View file

@ -1,7 +1,7 @@
// ==UserScript== // ==UserScript==
// @name HV Unified // @name HV Unified
// @namespace hvunified // @namespace hvunified
// @version 0.15.3 // @version 0.15.4
// @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.3'; const VERSION = '0.15.4';
const CFG = { const CFG = {
// — Battle automation // — Battle automation
@ -3528,7 +3528,16 @@ function parseEquipHTML(htmlStr) {
return; return;
} }
// Weapon damage line: "+708 Piercing Damage" // Elemental Strike line (wiki: "X Strike (Y%)" — e.g. "Void Strike (50%)")
// Additional ~50%-of-physical-damage hit; max 2 strikes + Void Strike.
const strikeM = text.match(/^([A-Za-z]+)\s+Strike\s*\(\s*(\d+)%\s*\)$/);
if (strikeM) {
stats['Strike ' + strikeM[1]] = parseInt(strikeM[2]);
return;
}
// Weapon damage line: "+523 Void Damage" / "+708 Piercing Damage"
// Base type can be Slashing/Crushing/Piercing, or Void for Ethereal.
const dmgM = text.match(/^\+([\d,]+)\s*([A-Za-z]+)\s*Damage$/); const dmgM = text.match(/^\+([\d,]+)\s*([A-Za-z]+)\s*Damage$/);
if (dmgM) { if (dmgM) {
stats['Weapon Damage'] = parseInt(dmgM[1].replace(/,/g, '')); stats['Weapon Damage'] = parseInt(dmgM[1].replace(/,/g, ''));
@ -3864,11 +3873,17 @@ 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 // Base weapon damage (e.g. "+708 Piercing Damage", "+523 Void Damage") — scales with quality
s += _num(st['Weapon Damage']) * 0.30; const wd = _num(st['Weapon Damage']);
// Elemental damage lines (e.g. "+25 Fire Damage", "+30 Elec Strike") s += wd * 0.30;
['Fire', 'Cold', 'Elec', 'Wind', 'Holy', 'Dark', 'Ethereal', 'Elemental'].forEach(el => { // Elemental Strikes (wiki: "X Strike (Y%)" — separate hit for ~50% of physical damage)
s += _num(st['Elemental ' + el]) * 0.25; // Expected damage = chance% × 0.5 × weapon damage. Weight same as base damage (0.30).
['Fire', 'Cold', 'Elec', 'Wind', 'Holy', 'Dark', 'Void', 'Ethereal'].forEach(el => {
const chance = _num(st['Strike ' + el]);
if (chance > 0) {
const expected = (chance / 100) * 0.5 * wd;
s += expected > 0 ? expected * 0.30 : chance * 0.15;
}
}); });
// Proc lines: elemental/status procs add value (e.g. "Proc Penetrated Armor: 21.7%") // Proc lines: elemental/status procs add value (e.g. "Proc Penetrated Armor: 21.7%")
let procBonus = 0; let procBonus = 0;
@ -3885,12 +3900,12 @@ function gearScoreWeapon(item) {
s += procBonus; 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 (wiki procs: Piercing→Penetrated Armor, Crushing→Stun,
// Slashing→Bleeding Wound; Estoc=2H Penetrated Armor is the 2H physical build's core)
if (name.includes('estoc')) s *= 1.25; if (name.includes('estoc')) s *= 1.25;
else if (name.includes('longsword')) s *= 1.10; else if (name.includes('rapier')) s *= 1.20; // 1H Penetrated Armor
else if (name.includes('great mace')) s *= 0.90; else if (name.includes('longsword') || name.includes('katana') || name.includes('shortsword') || name.includes('axe') || name.includes('wakizashi')) s *= 1.10; // Bleeding Wound
else if (name.includes('club')) s *= 0.85; else if (name.includes('great mace') || name.includes('club') || name.includes('mace')) s *= 1.15; // Stun
else if (name.includes('axe')) s *= 0.90;
return s; return s;
} }

View file

@ -1,7 +1,7 @@
// ==UserScript== // ==UserScript==
// @name HV Unified // @name HV Unified
// @namespace hvunified // @namespace hvunified
// @version 0.15.3 // @version 0.15.4
// @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.3'; const VERSION = '0.15.4';
const CFG = { const CFG = {
// — Battle automation // — Battle automation
@ -3528,7 +3528,16 @@ function parseEquipHTML(htmlStr) {
return; return;
} }
// Weapon damage line: "+708 Piercing Damage" // Elemental Strike line (wiki: "X Strike (Y%)" — e.g. "Void Strike (50%)")
// Additional ~50%-of-physical-damage hit; max 2 strikes + Void Strike.
const strikeM = text.match(/^([A-Za-z]+)\s+Strike\s*\(\s*(\d+)%\s*\)$/);
if (strikeM) {
stats['Strike ' + strikeM[1]] = parseInt(strikeM[2]);
return;
}
// Weapon damage line: "+523 Void Damage" / "+708 Piercing Damage"
// Base type can be Slashing/Crushing/Piercing, or Void for Ethereal.
const dmgM = text.match(/^\+([\d,]+)\s*([A-Za-z]+)\s*Damage$/); const dmgM = text.match(/^\+([\d,]+)\s*([A-Za-z]+)\s*Damage$/);
if (dmgM) { if (dmgM) {
stats['Weapon Damage'] = parseInt(dmgM[1].replace(/,/g, '')); stats['Weapon Damage'] = parseInt(dmgM[1].replace(/,/g, ''));
@ -3864,11 +3873,17 @@ 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 // Base weapon damage (e.g. "+708 Piercing Damage", "+523 Void Damage") — scales with quality
s += _num(st['Weapon Damage']) * 0.30; const wd = _num(st['Weapon Damage']);
// Elemental damage lines (e.g. "+25 Fire Damage", "+30 Elec Strike") s += wd * 0.30;
['Fire', 'Cold', 'Elec', 'Wind', 'Holy', 'Dark', 'Ethereal', 'Elemental'].forEach(el => { // Elemental Strikes (wiki: "X Strike (Y%)" — separate hit for ~50% of physical damage)
s += _num(st['Elemental ' + el]) * 0.25; // Expected damage = chance% × 0.5 × weapon damage. Weight same as base damage (0.30).
['Fire', 'Cold', 'Elec', 'Wind', 'Holy', 'Dark', 'Void', 'Ethereal'].forEach(el => {
const chance = _num(st['Strike ' + el]);
if (chance > 0) {
const expected = (chance / 100) * 0.5 * wd;
s += expected > 0 ? expected * 0.30 : chance * 0.15;
}
}); });
// Proc lines: elemental/status procs add value (e.g. "Proc Penetrated Armor: 21.7%") // Proc lines: elemental/status procs add value (e.g. "Proc Penetrated Armor: 21.7%")
let procBonus = 0; let procBonus = 0;
@ -3885,12 +3900,12 @@ function gearScoreWeapon(item) {
s += procBonus; 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 (wiki procs: Piercing→Penetrated Armor, Crushing→Stun,
// Slashing→Bleeding Wound; Estoc=2H Penetrated Armor is the 2H physical build's core)
if (name.includes('estoc')) s *= 1.25; if (name.includes('estoc')) s *= 1.25;
else if (name.includes('longsword')) s *= 1.10; else if (name.includes('rapier')) s *= 1.20; // 1H Penetrated Armor
else if (name.includes('great mace')) s *= 0.90; else if (name.includes('longsword') || name.includes('katana') || name.includes('shortsword') || name.includes('axe') || name.includes('wakizashi')) s *= 1.10; // Bleeding Wound
else if (name.includes('club')) s *= 0.85; else if (name.includes('great mace') || name.includes('club') || name.includes('mace')) s *= 1.15; // Stun
else if (name.includes('axe')) s *= 0.90;
return s; return s;
} }

View file

@ -2,7 +2,7 @@
// CONFIG — default settings // CONFIG — default settings
// ═══════════════════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════════════════
const VERSION = '0.15.3'; const VERSION = '0.15.4';
const CFG = { const CFG = {
// — Battle automation // — Battle automation

View file

@ -45,11 +45,17 @@ 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 // Base weapon damage (e.g. "+708 Piercing Damage", "+523 Void Damage") — scales with quality
s += _num(st['Weapon Damage']) * 0.30; const wd = _num(st['Weapon Damage']);
// Elemental damage lines (e.g. "+25 Fire Damage", "+30 Elec Strike") s += wd * 0.30;
['Fire', 'Cold', 'Elec', 'Wind', 'Holy', 'Dark', 'Ethereal', 'Elemental'].forEach(el => { // Elemental Strikes (wiki: "X Strike (Y%)" — separate hit for ~50% of physical damage)
s += _num(st['Elemental ' + el]) * 0.25; // Expected damage = chance% × 0.5 × weapon damage. Weight same as base damage (0.30).
['Fire', 'Cold', 'Elec', 'Wind', 'Holy', 'Dark', 'Void', 'Ethereal'].forEach(el => {
const chance = _num(st['Strike ' + el]);
if (chance > 0) {
const expected = (chance / 100) * 0.5 * wd;
s += expected > 0 ? expected * 0.30 : chance * 0.15;
}
}); });
// Proc lines: elemental/status procs add value (e.g. "Proc Penetrated Armor: 21.7%") // Proc lines: elemental/status procs add value (e.g. "Proc Penetrated Armor: 21.7%")
let procBonus = 0; let procBonus = 0;
@ -66,12 +72,12 @@ function gearScoreWeapon(item) {
s += procBonus; 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 (wiki procs: Piercing→Penetrated Armor, Crushing→Stun,
// Slashing→Bleeding Wound; Estoc=2H Penetrated Armor is the 2H physical build's core)
if (name.includes('estoc')) s *= 1.25; if (name.includes('estoc')) s *= 1.25;
else if (name.includes('longsword')) s *= 1.10; else if (name.includes('rapier')) s *= 1.20; // 1H Penetrated Armor
else if (name.includes('great mace')) s *= 0.90; else if (name.includes('longsword') || name.includes('katana') || name.includes('shortsword') || name.includes('axe') || name.includes('wakizashi')) s *= 1.10; // Bleeding Wound
else if (name.includes('club')) s *= 0.85; else if (name.includes('great mace') || name.includes('club') || name.includes('mace')) s *= 1.15; // Stun
else if (name.includes('axe')) s *= 0.90;
return s; return s;
} }

View file

@ -133,7 +133,16 @@ function parseEquipHTML(htmlStr) {
return; return;
} }
// Weapon damage line: "+708 Piercing Damage" // Elemental Strike line (wiki: "X Strike (Y%)" — e.g. "Void Strike (50%)")
// Additional ~50%-of-physical-damage hit; max 2 strikes + Void Strike.
const strikeM = text.match(/^([A-Za-z]+)\s+Strike\s*\(\s*(\d+)%\s*\)$/);
if (strikeM) {
stats['Strike ' + strikeM[1]] = parseInt(strikeM[2]);
return;
}
// Weapon damage line: "+523 Void Damage" / "+708 Piercing Damage"
// Base type can be Slashing/Crushing/Piercing, or Void for Ethereal.
const dmgM = text.match(/^\+([\d,]+)\s*([A-Za-z]+)\s*Damage$/); const dmgM = text.match(/^\+([\d,]+)\s*([A-Za-z]+)\s*Damage$/);
if (dmgM) { if (dmgM) {
stats['Weapon Damage'] = parseInt(dmgM[1].replace(/,/g, '')); stats['Weapon Damage'] = parseInt(dmgM[1].replace(/,/g, ''));

View file

@ -1,7 +1,7 @@
// ==UserScript== // ==UserScript==
// @name HV Unified // @name HV Unified
// @namespace hvunified // @namespace hvunified
// @version 0.15.3 // @version 0.15.4
// @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/*