v0.14.17 - Faster weapon skills: lower skillOC to 60, threat to 70%, single-monster emergency
- skillOC default lowered from 75 to 60 — with 300ms Q debounce, it took ~6 presses to go from 50 to 70 OC (too slow against IWBTH enemies) - Threat threshold lowered from 85% (102px) to 70% (84px) — triggers earlier when monsters have high SP bars - Added single-monster emergency: if ANY enemy >90% SP, fire skills even without 2+ monsters (catches the lone special attacker) - Step 0d high-threat check now fires for solitary imminent threats too
This commit is contained in:
parent
4aff813f01
commit
f2f296274c
7 changed files with 190 additions and 21 deletions
159
analyze_gear.py
Normal file
159
analyze_gear.py
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Analyze HV gear database for 2H physical build optimization."""
|
||||||
|
import json, sys
|
||||||
|
|
||||||
|
def get_num(val):
|
||||||
|
if not val: return 0
|
||||||
|
s = str(val).replace('+','').replace('%','').strip()
|
||||||
|
try: return float(s)
|
||||||
|
except: return 0
|
||||||
|
|
||||||
|
def score_armor(item):
|
||||||
|
stats = item.get('stats',{})
|
||||||
|
s = get_num(stats.get('Physical Mitigation',0)) * 3
|
||||||
|
s += get_num(stats.get('Evade',0)) * 2
|
||||||
|
s += get_num(stats.get('Strength',0)) * 2
|
||||||
|
s += get_num(stats.get('Dexterity',0)) * 2
|
||||||
|
s += get_num(stats.get('Agility',0)) * 1.5
|
||||||
|
s += get_num(stats.get('Endurance',0)) * 1
|
||||||
|
s += get_num(stats.get('Crushing',0))
|
||||||
|
s += get_num(stats.get('Slashing',0))
|
||||||
|
s += get_num(stats.get('Piercing',0))
|
||||||
|
s -= get_num(stats.get('burden',0)) * 0.5
|
||||||
|
s += item.get('quality',0) * 5
|
||||||
|
return s
|
||||||
|
|
||||||
|
# Map item names to equipment slots by keyword
|
||||||
|
SLOT_KEYWORDS = {
|
||||||
|
'Head': ['helmet','cap','goggles','mask','hood','hat','circlet','crown','visor'],
|
||||||
|
'Body': ['breastplate','cuirass','robe','tunic','chest','gi','jacket','vest','hauberk'],
|
||||||
|
'Hands': ['gauntlets','gloves','mitts','bracers','handguards','fists'],
|
||||||
|
'Legs': ['leggings','greaves','pants','trousers','loincloth','kilt','breeches','chaps'],
|
||||||
|
'Feet': ['boots','sabatons','shoes','slippers','sandals'],
|
||||||
|
}
|
||||||
|
|
||||||
|
def detect_slot(item):
|
||||||
|
"""Guess equipment slot from item name."""
|
||||||
|
name = (item.get('name','') + ' ' + item.get('category','')).lower()
|
||||||
|
for slot, keywords in SLOT_KEYWORDS.items():
|
||||||
|
for kw in keywords:
|
||||||
|
if kw in name:
|
||||||
|
return slot
|
||||||
|
stats = item.get('stats',{})
|
||||||
|
# Fallback: use proportional scoring
|
||||||
|
return None
|
||||||
|
|
||||||
|
def is_twohand_weapon(item):
|
||||||
|
"""Check if item is a 2H weapon (not shield, not armor, not 1H)."""
|
||||||
|
stats = item.get('stats',{})
|
||||||
|
itype = stats.get('type','')
|
||||||
|
# Exclude armor types
|
||||||
|
if 'Armor' in itype or 'Shield' in itype:
|
||||||
|
return False
|
||||||
|
# Check for 2H weapon keywords
|
||||||
|
name_lower = (item.get('name','') + ' ' + item.get('category','')).lower()
|
||||||
|
if any(k in name_lower for k in ['estoc','longsword','great mace','scythe','axe','club']):
|
||||||
|
return True
|
||||||
|
# Fallback: burden >= 14 AND has attack accuracy (not burden from armor)
|
||||||
|
if get_num(stats.get('burden',0)) >= 14 and 'attack accuracy' in str(stats).lower():
|
||||||
|
# Double check it's not armor with attack damage bonus
|
||||||
|
if 'physical mitigation' not in str(stats).lower():
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def score_weapon(item):
|
||||||
|
stats = item.get('stats',{})
|
||||||
|
s = get_num(stats.get('Attack Accuracy',0)) * 3
|
||||||
|
s += get_num(stats.get('Attack Crit Damage',0)) * 80
|
||||||
|
s += get_num(stats.get('Strength',0)) * 2
|
||||||
|
s += get_num(stats.get('Parry',0))
|
||||||
|
s += get_num(stats.get('Block',0))
|
||||||
|
s += get_num(stats.get('Agility',0))
|
||||||
|
s += get_num(stats.get('Dexterity',0))
|
||||||
|
s -= get_num(stats.get('burden',0)) * 0.5
|
||||||
|
s += item.get('quality',0) * 5
|
||||||
|
return s
|
||||||
|
|
||||||
|
# Filter items by usable level (within 20 levels of player level 153)
|
||||||
|
def usable(item):
|
||||||
|
stats = item.get('stats',{})
|
||||||
|
lv = stats.get('level','0')
|
||||||
|
if lv == 'Unassigned': return True
|
||||||
|
try: lv = int(lv)
|
||||||
|
except: return True
|
||||||
|
return lv <= 170 # allow up to L170 (within reach)
|
||||||
|
|
||||||
|
with open('references/gear.json') as f:
|
||||||
|
gear = json.load(f)
|
||||||
|
|
||||||
|
equipped = [i for i in gear if i.get('source') == 'character' and i.get('id') and not i.get('disabled')]
|
||||||
|
armory = [i for i in gear if i.get('source') == 'armory' and i.get('stats')]
|
||||||
|
|
||||||
|
slot_map = {e.get('slotType',''): e for e in equipped}
|
||||||
|
|
||||||
|
print("=" * 70)
|
||||||
|
print("2H PHYSICAL BUILD — GEAR ANALYSIS (Lv153, Estoc, IWBTH)")
|
||||||
|
print("=" * 70)
|
||||||
|
|
||||||
|
for slot_type in ['Mainhand', 'Head', 'Body', 'Hands', 'Legs', 'Feet']:
|
||||||
|
current = slot_map.get(slot_type)
|
||||||
|
if not current:
|
||||||
|
print(f"\n🛡️ {slot_type}: No equipped item")
|
||||||
|
continue
|
||||||
|
print(f"\n━━━ {slot_type} ━━━")
|
||||||
|
cur_stats = current.get('stats',{})
|
||||||
|
print(f" 🗡 {current['name']} (q{current.get('quality','?')})")
|
||||||
|
|
||||||
|
candidates = []
|
||||||
|
for item in armory:
|
||||||
|
stats = item.get('stats',{})
|
||||||
|
if not usable(item): continue
|
||||||
|
if slot_type == 'Mainhand':
|
||||||
|
if not is_twohand_weapon(item): continue
|
||||||
|
sfunc = score_weapon
|
||||||
|
else:
|
||||||
|
if stats.get('type') != 'Light Armor': continue
|
||||||
|
# Match by slot type
|
||||||
|
detected = detect_slot(item)
|
||||||
|
if detected != slot_type: continue
|
||||||
|
sfunc = score_armor
|
||||||
|
candidates.append((sfunc(item), item))
|
||||||
|
|
||||||
|
candidates.sort(key=lambda x: -x[0])
|
||||||
|
|
||||||
|
for score, item in candidates[:5]:
|
||||||
|
stats = item.get('stats',{})
|
||||||
|
marker = ' ⬅ EQUIPPED' if item['name'] == current['name'] else ''
|
||||||
|
if slot_type == 'Mainhand':
|
||||||
|
print(f" {score:7.1f} | {item['name']} ({item.get('quality','?')}){marker}")
|
||||||
|
print(f" Acc:{stats.get('Attack Accuracy','?')} CritDam:{stats.get('Attack Crit Damage','?')} STR:{stats.get('Strength','?')} B:{stats.get('burden','?')}")
|
||||||
|
else:
|
||||||
|
print(f" {score:7.1f} | {item['name']} (q{item.get('quality','?')}) | Lv{stats.get('level','?')}{marker}")
|
||||||
|
print(f" PMit:{stats.get('Physical Mitigation','?')} Evade:{stats.get('Evade','?')} STR:{stats.get('Strength','?')} B:{stats.get('burden','?')}")
|
||||||
|
|
||||||
|
print("\n" + "=" * 70)
|
||||||
|
print("🏆 UPGRADE RECOMMENDATIONS")
|
||||||
|
print("=" * 70)
|
||||||
|
for slot_type in ['Mainhand', 'Head', 'Body', 'Hands', 'Legs', 'Feet']:
|
||||||
|
current = slot_map.get(slot_type)
|
||||||
|
if not current: continue
|
||||||
|
candidates = []
|
||||||
|
for item in armory:
|
||||||
|
stats = item.get('stats',{})
|
||||||
|
if not stats.get('type'): continue
|
||||||
|
if not usable(item): continue
|
||||||
|
if slot_type == 'Mainhand':
|
||||||
|
if not is_twohand_weapon(item): continue
|
||||||
|
else:
|
||||||
|
if stats['type'] != 'Light Armor': continue
|
||||||
|
if detect_slot(item) != slot_type: continue
|
||||||
|
sfunc = score_weapon if slot_type == 'Mainhand' else score_armor
|
||||||
|
candidates.append((sfunc(item), item))
|
||||||
|
candidates.sort(key=lambda x: -x[0])
|
||||||
|
if not candidates: continue
|
||||||
|
top = candidates[0]
|
||||||
|
if top[1]['name'] != current['name']:
|
||||||
|
print(f"\n❌ {slot_type}: {current['name']}")
|
||||||
|
print(f"✅ → {top[1]['name']} (q{top[1].get('quality','?')}, score {top[0]:.1f})")
|
||||||
|
else:
|
||||||
|
print(f"\n✅ {slot_type}: {current['name']} — already best (score {top[0]:.1f})")
|
||||||
1
references/gear.json
Normal file
1
references/gear.json
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1,7 +1,7 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name HV Unified
|
// @name HV Unified
|
||||||
// @namespace hvunified
|
// @namespace hvunified
|
||||||
// @version 0.14.16
|
// @version 0.14.17
|
||||||
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
||||||
// @author GaboGG + Hermes
|
// @author GaboGG + Hermes
|
||||||
// @match *://*.hentaiverse.org/*
|
// @match *://*.hentaiverse.org/*
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
// CONFIG — default settings
|
// CONFIG — default settings
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
const VERSION = '0.14.16';
|
const VERSION = '0.14.17';
|
||||||
|
|
||||||
const CFG = {
|
const CFG = {
|
||||||
// — Battle automation
|
// — Battle automation
|
||||||
|
|
@ -37,7 +37,7 @@ const CFG = {
|
||||||
manaPotionMP: 0.25, // Use mana potions at this MP
|
manaPotionMP: 0.25, // Use mana potions at this MP
|
||||||
spiritPotionSP: 0.60, // Use spirit gems/potions below this SP (Spark costs 50%)
|
spiritPotionSP: 0.60, // Use spirit gems/potions below this SP (Spark costs 50%)
|
||||||
spiritStanceOC: 80, // Manual spirit stance only (auto-disable at 65% SP for Spark)
|
spiritStanceOC: 80, // Manual spirit stance only (auto-disable at 65% SP for Spark)
|
||||||
skillOC: 75, // Use weapon skills at this OC
|
skillOC: 60, // Use weapon skills at this OC (lower = faster, key on IWBTH)
|
||||||
useAttackSpells: false, // Don't use attack spells (let channel proc naturally)
|
useAttackSpells: false, // Don't use attack spells (let channel proc naturally)
|
||||||
|
|
||||||
// — Out of battle
|
// — Out of battle
|
||||||
|
|
@ -770,8 +770,8 @@ function shouldBuff(name, minTurns) {
|
||||||
|
|
||||||
// Known buffs with their priority, icon name, spell name, and refresh threshold
|
// Known buffs with their priority, icon name, spell name, and refresh threshold
|
||||||
|
|
||||||
// Threat detection: SP bar max is 120px, monsters >85% are about to special
|
// Threat detection: SP bar max is 120px, monsters >70% are dangerous
|
||||||
const THREAT_PX = Math.round(120 * 85 / 100); // ~102px
|
const THREAT_PX = Math.round(120 * 70 / 100); // ~84px — triggers earlier on IWBTH
|
||||||
|
|
||||||
// NOTE: Heartseeker and Arcane Focus are mutually exclusive. For a 2H (physical) build,
|
// NOTE: Heartseeker and Arcane Focus are mutually exclusive. For a 2H (physical) build,
|
||||||
// Heartseeker is preferred (+25% phys dmg, +10% crit). Arcane Focus is for magic builds.
|
// Heartseeker is preferred (+25% phys dmg, +10% crit). Arcane Focus is for magic builds.
|
||||||
|
|
@ -1429,9 +1429,12 @@ function strategyVeteran() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0d. HIGH-THREAT WEAPON SKILLS — before buffs/items/debuffs
|
// 0d. HIGH-THREAT WEAPON SKILLS — before buffs/items/debuffs
|
||||||
// If 2+ enemies have >85% SP bar, kill/stun them NOW before they
|
// If enemies have high SP bars, kill/stun them NOW before they
|
||||||
// use special attacks. Don't waste turns buffing while monsters charge.
|
// use special attacks. Don't waste turns buffing while monsters charge.
|
||||||
if (hasHighThreat(THREAT_PX) && STATE.oc >= 20) {
|
const SINGLE_THREAT = Math.round(120 * 90 / 100); // 108px = one monster about to special
|
||||||
|
const anyImminent = STATE.monsters.some((m, i) => (STATE.monsterSp[i] || 0) > SINGLE_THREAT);
|
||||||
|
const isHighThreatEarly = hasHighThreat(THREAT_PX);
|
||||||
|
if ((isHighThreatEarly || anyImminent) && STATE.oc >= 20) {
|
||||||
// Rending Blow: AoE armor pen vs 3+ enemies
|
// Rending Blow: AoE armor pen vs 3+ enemies
|
||||||
if (STATE.monsters.length >= 3 && STATE.skillsKnown.includes('Rending Blow')) {
|
if (STATE.monsters.length >= 3 && STATE.skillsKnown.includes('Rending Blow')) {
|
||||||
const t = findDangerousMonster();
|
const t = findDangerousMonster();
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name HV Unified
|
// @name HV Unified
|
||||||
// @namespace hvunified
|
// @namespace hvunified
|
||||||
// @version 0.14.16
|
// @version 0.14.17
|
||||||
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
||||||
// @author GaboGG + Hermes
|
// @author GaboGG + Hermes
|
||||||
// @match *://*.hentaiverse.org/*
|
// @match *://*.hentaiverse.org/*
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
// CONFIG — default settings
|
// CONFIG — default settings
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
const VERSION = '0.14.16';
|
const VERSION = '0.14.17';
|
||||||
|
|
||||||
const CFG = {
|
const CFG = {
|
||||||
// — Battle automation
|
// — Battle automation
|
||||||
|
|
@ -37,7 +37,7 @@ const CFG = {
|
||||||
manaPotionMP: 0.25, // Use mana potions at this MP
|
manaPotionMP: 0.25, // Use mana potions at this MP
|
||||||
spiritPotionSP: 0.60, // Use spirit gems/potions below this SP (Spark costs 50%)
|
spiritPotionSP: 0.60, // Use spirit gems/potions below this SP (Spark costs 50%)
|
||||||
spiritStanceOC: 80, // Manual spirit stance only (auto-disable at 65% SP for Spark)
|
spiritStanceOC: 80, // Manual spirit stance only (auto-disable at 65% SP for Spark)
|
||||||
skillOC: 75, // Use weapon skills at this OC
|
skillOC: 60, // Use weapon skills at this OC (lower = faster, key on IWBTH)
|
||||||
useAttackSpells: false, // Don't use attack spells (let channel proc naturally)
|
useAttackSpells: false, // Don't use attack spells (let channel proc naturally)
|
||||||
|
|
||||||
// — Out of battle
|
// — Out of battle
|
||||||
|
|
@ -770,8 +770,8 @@ function shouldBuff(name, minTurns) {
|
||||||
|
|
||||||
// Known buffs with their priority, icon name, spell name, and refresh threshold
|
// Known buffs with their priority, icon name, spell name, and refresh threshold
|
||||||
|
|
||||||
// Threat detection: SP bar max is 120px, monsters >85% are about to special
|
// Threat detection: SP bar max is 120px, monsters >70% are dangerous
|
||||||
const THREAT_PX = Math.round(120 * 85 / 100); // ~102px
|
const THREAT_PX = Math.round(120 * 70 / 100); // ~84px — triggers earlier on IWBTH
|
||||||
|
|
||||||
// NOTE: Heartseeker and Arcane Focus are mutually exclusive. For a 2H (physical) build,
|
// NOTE: Heartseeker and Arcane Focus are mutually exclusive. For a 2H (physical) build,
|
||||||
// Heartseeker is preferred (+25% phys dmg, +10% crit). Arcane Focus is for magic builds.
|
// Heartseeker is preferred (+25% phys dmg, +10% crit). Arcane Focus is for magic builds.
|
||||||
|
|
@ -1429,9 +1429,12 @@ function strategyVeteran() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0d. HIGH-THREAT WEAPON SKILLS — before buffs/items/debuffs
|
// 0d. HIGH-THREAT WEAPON SKILLS — before buffs/items/debuffs
|
||||||
// If 2+ enemies have >85% SP bar, kill/stun them NOW before they
|
// If enemies have high SP bars, kill/stun them NOW before they
|
||||||
// use special attacks. Don't waste turns buffing while monsters charge.
|
// use special attacks. Don't waste turns buffing while monsters charge.
|
||||||
if (hasHighThreat(THREAT_PX) && STATE.oc >= 20) {
|
const SINGLE_THREAT = Math.round(120 * 90 / 100); // 108px = one monster about to special
|
||||||
|
const anyImminent = STATE.monsters.some((m, i) => (STATE.monsterSp[i] || 0) > SINGLE_THREAT);
|
||||||
|
const isHighThreatEarly = hasHighThreat(THREAT_PX);
|
||||||
|
if ((isHighThreatEarly || anyImminent) && STATE.oc >= 20) {
|
||||||
// Rending Blow: AoE armor pen vs 3+ enemies
|
// Rending Blow: AoE armor pen vs 3+ enemies
|
||||||
if (STATE.monsters.length >= 3 && STATE.skillsKnown.includes('Rending Blow')) {
|
if (STATE.monsters.length >= 3 && STATE.skillsKnown.includes('Rending Blow')) {
|
||||||
const t = findDangerousMonster();
|
const t = findDangerousMonster();
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// CONFIG — default settings
|
// CONFIG — default settings
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
const VERSION = '0.14.16';
|
const VERSION = '0.14.17';
|
||||||
|
|
||||||
const CFG = {
|
const CFG = {
|
||||||
// — Battle automation
|
// — Battle automation
|
||||||
|
|
@ -22,7 +22,7 @@ const CFG = {
|
||||||
manaPotionMP: 0.25, // Use mana potions at this MP
|
manaPotionMP: 0.25, // Use mana potions at this MP
|
||||||
spiritPotionSP: 0.60, // Use spirit gems/potions below this SP (Spark costs 50%)
|
spiritPotionSP: 0.60, // Use spirit gems/potions below this SP (Spark costs 50%)
|
||||||
spiritStanceOC: 80, // Manual spirit stance only (auto-disable at 65% SP for Spark)
|
spiritStanceOC: 80, // Manual spirit stance only (auto-disable at 65% SP for Spark)
|
||||||
skillOC: 75, // Use weapon skills at this OC
|
skillOC: 60, // Use weapon skills at this OC (lower = faster, key on IWBTH)
|
||||||
useAttackSpells: false, // Don't use attack spells (let channel proc naturally)
|
useAttackSpells: false, // Don't use attack spells (let channel proc naturally)
|
||||||
|
|
||||||
// — Out of battle
|
// — Out of battle
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name HV Unified
|
// @name HV Unified
|
||||||
// @namespace hvunified
|
// @namespace hvunified
|
||||||
// @version 0.14.16
|
// @version 0.14.17
|
||||||
// @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/*
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,8 @@ function shouldBuff(name, minTurns) {
|
||||||
|
|
||||||
// Known buffs with their priority, icon name, spell name, and refresh threshold
|
// Known buffs with their priority, icon name, spell name, and refresh threshold
|
||||||
|
|
||||||
// Threat detection: SP bar max is 120px, monsters >85% are about to special
|
// Threat detection: SP bar max is 120px, monsters >70% are dangerous
|
||||||
const THREAT_PX = Math.round(120 * 85 / 100); // ~102px
|
const THREAT_PX = Math.round(120 * 70 / 100); // ~84px — triggers earlier on IWBTH
|
||||||
|
|
||||||
// NOTE: Heartseeker and Arcane Focus are mutually exclusive. For a 2H (physical) build,
|
// NOTE: Heartseeker and Arcane Focus are mutually exclusive. For a 2H (physical) build,
|
||||||
// Heartseeker is preferred (+25% phys dmg, +10% crit). Arcane Focus is for magic builds.
|
// Heartseeker is preferred (+25% phys dmg, +10% crit). Arcane Focus is for magic builds.
|
||||||
|
|
@ -704,9 +704,12 @@ function strategyVeteran() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0d. HIGH-THREAT WEAPON SKILLS — before buffs/items/debuffs
|
// 0d. HIGH-THREAT WEAPON SKILLS — before buffs/items/debuffs
|
||||||
// If 2+ enemies have >85% SP bar, kill/stun them NOW before they
|
// If enemies have high SP bars, kill/stun them NOW before they
|
||||||
// use special attacks. Don't waste turns buffing while monsters charge.
|
// use special attacks. Don't waste turns buffing while monsters charge.
|
||||||
if (hasHighThreat(THREAT_PX) && STATE.oc >= 20) {
|
const SINGLE_THREAT = Math.round(120 * 90 / 100); // 108px = one monster about to special
|
||||||
|
const anyImminent = STATE.monsters.some((m, i) => (STATE.monsterSp[i] || 0) > SINGLE_THREAT);
|
||||||
|
const isHighThreatEarly = hasHighThreat(THREAT_PX);
|
||||||
|
if ((isHighThreatEarly || anyImminent) && STATE.oc >= 20) {
|
||||||
// Rending Blow: AoE armor pen vs 3+ enemies
|
// Rending Blow: AoE armor pen vs 3+ enemies
|
||||||
if (STATE.monsters.length >= 3 && STATE.skillsKnown.includes('Rending Blow')) {
|
if (STATE.monsters.length >= 3 && STATE.skillsKnown.includes('Rending Blow')) {
|
||||||
const t = findDangerousMonster();
|
const t = findDangerousMonster();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue