/*******************************************************************************
* Copyright (c) 2014, 2015 Scott Clarke (scott@dawg6.com).
*
* This file is part of Dawg6's Demon Hunter DPS Calculator.
*
* Dawg6's Demon Hunter DPS Calculator is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Dawg6's Demon Hunter DPS Calculator is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
package com.dawg6.web.dhcalc.shared.calculator;
public enum DamageMultiplier {
WD("WD", DamageAccumulator.Multiplicative,
"Average Main Hand Weapon Damage",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getWeaponDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.getWeaponMaximumDamage();
}
}), MAXWD("MaxWD", DamageAccumulator.Multiplicative,
"Maximum Main Hand Weapon Damage",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getWeaponMaximumDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.getWeaponMaximumDamage();
}
}), OHWD("OHWD", DamageAccumulator.Multiplicative,
"Average Off-Hand Weapon Damage",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getOffHand_weaponDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.getOffHand_maxDamage();
}
}), HW("HW", DamageAccumulator.Multiplicative,
"Hunter's Wrath (45%-60% for primary skills)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().isHuntersWrath() ? state.getData()
.getHuntersWrathPercent() : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return ((row.source.skill.getSkillType() == SkillType.Primary) && data
.isHuntersWrath()) ? data.getHuntersWrathPercent()
: 0.0;
}
}), Hellcat("HCB", DamageAccumulator.Multiplicative,
"Hellcat Bounces (2-4 x 50% + 1 x 800%)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getHellcatDamageMultiplier();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (!sentry && row.multipliers
.contains(DamageMultiplier.Grenades)) ? data
.getHellcatDamageMultiplier() : 0.0;
}
}), PC("PC", DamageAccumulator.Multiplicative,
"Proc Coefficient (depends on skill)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getLastAttack().getProc();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.source.rune == Rune.None) ? row.source.skill
.getProc() : row.source.rune.getProc();
}
}), LGF(
"LGF",
DamageAccumulator.Multiplicative,
"Lord Greenstone's Fan bonus to Fan of Knives (+160-200%, per stack)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().isLGF() ? (state.getLGFStacks() * state
.getData().getLGFPercent()) : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return ((row.source.skill == ActiveSkill.FoK) && data
.isLGF()) ? (data.getLGFPercent() * 30.0) : 0.0;
}
}), DD("DD", DamageAccumulator.Multiplicative,
"Depth Diggers (80%-100% for primary skills)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().isDepthDiggers() ? state.getData()
.getDepthDiggersPercent() : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return ((row.source.skill.getSkillType() == SkillType.Primary) && data
.isDepthDiggers()) ? data.getDepthDiggersPercent()
: 0.0;
}
}), NumGrenades("#G", DamageAccumulator.Multiplicative,
"# of Grenades per Target", null), Fire("Fire",
DamageAccumulator.ElementalAdditive, "Fire Elemental Damage Bonus",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getFireDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.type == DamageType.Fire) ? data.getFireDamage()
: 0.0;
}
}), Augustines("AP", DamageAccumulator.Multiplicative,
"Augustine's Panacea Damage Bonus (200%-250%)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().isAugustinesPanacea() ? state
.getData().getAugustinesPanaceaPercent() : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return ((row.source.skill != ActiveSkill.EA)
|| (row.source.rune == Rune.None) || (row.source.rune == Rune.Ball_Lightning)) ? 0.0
: data.getAugustinesPanaceaPercent();
}
}), Cold("Cold", DamageAccumulator.ElementalAdditive,
"Cold Elemental Damage Bonus", new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getColdDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.type == DamageType.Cold) ? data.getColdDamage()
: 0.0;
}
}), Physical("Physical", DamageAccumulator.ElementalAdditive,
"Physical Damage Bonus", new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getPhysDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.type == DamageType.Physical) ? data
.getPhysDamage() : 0.0;
}
}), SharpShooter(
"CCX",
DamageAccumulator.Special,
"Effective Crit Chance Bonus from Sharpshooter and/or Broken Promises",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState data) {
return (data.getData().isSharpshooter() || data.getData()
.isBrokenPromises()) ? data.getData()
.getSharpshooterCC() : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (data.isSharpshooter() || data.isBrokenPromises()) ? 1.0
: 0.0;
}
}), Lightning("Lightning", DamageAccumulator.ElementalAdditive,
"Lightning Elemental Damage Bonus",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getLightDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.type == DamageType.Lightning) ? data
.getLightDamage() : 0.0;
}
}), Poison("Poison", DamageAccumulator.ElementalAdditive,
"Poison Elemental Damage Bonus",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getPoisonDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.type == DamageType.Poison) ? data
.getPoisonDamage() : 0.0;
}
}), Holy("Holy", DamageAccumulator.ElementalAdditive,
"Holy Elemental Damage Bonus", new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getHolyDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.type == DamageType.Holy) ? data.getHolyDamage()
: 0.0;
}
}), Enforcer(
"Enforcer",
DamageAccumulator.Multiplicative,
"Enforcer gem bonus (15% + 3%/rank pet damage). Does not apply to Sentry Spitfire rockets.",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().isUseEnforcer() ? (0.15 + (state
.getData().getEnforcerLevel() * 0.003)) : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (((row.source.skill == ActiveSkill.Companion)
|| (row.source.skill == ActiveSkill.SENTRY) || (row.source.skill == ActiveSkill.BOLT)) && data
.isUseEnforcer()) ? (0.15 + (data
.getEnforcerLevel() * 0.003)) : 0.0;
}
}), Zeis(
"Zei's",
DamageAccumulator.Multiplicative,
"Zei's stone of vengeance gem bonus (4% + .08%/rank per 10 yards up to 50 yards).",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
if (!state.getData().isZeis())
return 0.0;
int d = Math.min(
state.getData().getDistanceToTarget() / 10, 5);
return d
* (0.04 + (state.getData().getZeisLevel() * 0.0008));
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (data.isZeis() && (row.source.skill != ActiveSkill.Companion)) ? (5 * (0.04 + (data
.getZeisLevel() * 0.0008))) : 0.0;
}
}), Traps("Traps", DamageAccumulator.Multiplicative,
"# of Other Traps (Spike Trap arcs)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
double n = state.getNumSpikeTraps();
return (n > 1) ? (n - 2) : 0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return 0.0;
}
}), M4("M4", DamageAccumulator.Multiplicative,
"M4 Damage Bonus for Sentries (400%)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return (state.getData().getNumMarauders() >= 4) ? 4.0 : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (sentry && (data.getNumMarauders() >= 4)) ? 4.0
: 0.0;
}
}), SentryM4("SentryM4", DamageAccumulator.Multiplicative,
"Sentry Skill Damage Bonus for skills fired by M4",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getSentryDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (sentry && (row.source.skill.getSkillType() == SkillType.Spender)) ? data
.getSentryDamage() : 0.0;
}
}), Sentry("Sentry", DamageAccumulator.Additive,
"Sentry Skill Damage Bonus for sentry bolts/rockets",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getSentryDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (sentry && ((row.source.skill == ActiveSkill.SENTRY) || (row.source.skill == ActiveSkill.BOLT))) ? data
.getSentryDamage() : 0.0;
}
}), COE("CoE", DamageAccumulator.Multiplicative,
"Convention of Elements Bonus (150-200% while element is active)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().isCoe() ? state.getData()
.getCoePercent() : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.isCoe() ? data.getCoePercent() : 0.0;
}
}), BL(
"BL",
DamageAccumulator.Multiplicative,
"Ball Lightning Damage Multiplier (varies based on target size and Meticulous Bolts %)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
double m = state.getData().getTargetSize().getHits() / 2.0;
if (state.getData().isMeticulousBolts()) {
double ratio = 1.0 / state.getData()
.getMeticulousBoltsPercent();
m *= ratio;
}
return m - 1.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
if ((row.source.skill != ActiveSkill.EA)
|| (row.source.rune != Rune.Ball_Lightning)
|| !data.isMeticulousBolts())
return 0.0;
double m = TargetSize.Large.getHits() / 2.0;
double ratio = 1.0 / data.getMeticulousBoltsPercent();
return (m * ratio) - 1.0;
}
}), Rockets("Ballistics", DamageAccumulator.Multiplicative,
"Ballistics passive bonus (100% Rocket damage)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().isBallistics() ? 1.0 : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.multipliers.contains(Rockets) && data
.isBallistics()) ? 1.0 : 0.0;
}
}), Grenades("Grenadier", DamageAccumulator.Multiplicative,
"Grenadier passive bonus (10% to grenade damage)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().isGrenadier() ? 0.1 : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.multipliers.contains(Grenades) && data
.isGrenadier()) ? 0.1 : 0.0;
}
}), SteadyAim("SteadyAim", DamageAccumulator.Multiplicative,
"Steady Aim passive bonus (20% when no enemies within 10 yards)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return (state.getData().isSteadyAim()) ? (0.2 * state
.getData().getPercentAtLeast10Yards()) : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.isSteadyAim() ? 0.2 : 0.0;
}
}), Strongarm("Strongarm", DamageAccumulator.Additive,
"Strongarm Bracers bonus (20-30% during uptime)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return (state.getData().isStrongarm()) ? (state.getData()
.getStrongarmPercent() * state.getData()
.getStrongarmUptime()) : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.isStrongarm() ? data.getStrongarmPercent()
: 0.0;
}
}), Harrington("Harrington", DamageAccumulator.Additive,
"Harrington Waistguard bonus (100-135% during uptime)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return (state.getData().isHarrington()) ? (state.getData()
.getHarringtonPercent() * state.getData()
.getHarringtonUptime()) : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.isHarrington() ? data.getHarringtonPercent()
: 0.0;
}
}), HexingPants(
"HexingPants",
DamageAccumulator.Additive,
"Hexing Pants of Mr. Yan bonus (+ while moving, - while stationary)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().isHexingPants() ? ((0.25 * state
.getData().getPercentMoving()) - (state.getData()
.getHexingPantsPercent() * (1.0 - state.getData()
.getPercentMoving()))) : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.isHexingPants() ? 0.25 : 0.0;
}
}), EW("EW", DamageAccumulator.Multiplicative,
"Endless Walk (up to +100% while stationary)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().isEndlessWalk() ? (1.0 * (1.0 - state
.getData().getPercentMoving())) : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.isEndlessWalk() ? 1.0 : 0.0;
}
}), ArcheryDamage("Archery", DamageAccumulator.Additive,
"Archery damage bonus (8% when using 2H Bow)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return (state.getData().isArchery() && (state.getData()
.getWeaponType() == WeaponType.Bow)) ? 0.08 : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (data.isArchery() && (data.getWeaponType() == WeaponType.Bow)) ? 0.08
: 0.0;
}
}), UE4(
"UE4",
DamageAccumulator.Multiplicative,
"Unhallowed Essence 4 item set bonus (60% if no enemies within 10 yards)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return (state.getData().getNumUe() >= 4) ? (0.6 * state
.getData().getPercentAtLeast10Yards()) : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (data.getNumUe() >= 4) ? 0.6 : 0.0;
}
}), UE6(
"UE6",
DamageAccumulator.Multiplicative,
"Unhallowed Essence 6 item set bonus (40% per point of discipline)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return (state.getData().getNumUe() >= 6) ? (0.4 * state
.getDisc()) : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return ((data.getNumUe() >= 6)
&& ((row.source.skill == ActiveSkill.MS) || ((row.source.skill == ActiveSkill.Vengeance) || (new SkillAndRune(
row.source.skill, row.source.rune)
.getHatred(data) > 0))) && !sentry) ? (0.4 * data
.getMaxDiscipline()) : 0.0;
}
}), BW1("BWg", DamageAccumulator.Multiplicative,
"Bastions of Will Generator Bonus (50%)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getBuffs().isActive(Buff.BwGen) ? 0.5 : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.isBastions() ? 0.5 : 0.0;
}
}), BW2("BWs", DamageAccumulator.Multiplicative,
"Bastions of Will Spender Bonus (50%)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getBuffs().isActive(Buff.BwSpend) ? 0.5 : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.isBastions() ? 0.5 : 0.0;
}
}), Occulus("Oculus", DamageAccumulator.Multiplicative,
"Oculus Ring Bonus(70-85% while Active)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
boolean a = state.getData().isOculus();
boolean b = state.getData().isFollowerOculus();
boolean c = state.getData().isPartyOculus();
if (!a && !b && !c)
return 0.0;
double t = 0.0;
int n = 0;
if (a) {
t += (state.getData().getOculusPercent() * state
.getData().getOculusUptime());
n++;
}
if (b) {
t += (state.getData().getFollowerOculusPercent() * state
.getData().getFollowerOculusUptime());
n++;
}
if (c) {
t += (state.getData().getPartyOculusPercent() * state
.getData().getPartyOculusUptime());
n++;
}
return t / n;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return Math.max(data.isOculus() ? data.getOculusPercent()
: 0.0, Math.max(
data.isFollowerOculus() ? data
.getFollowerOculusPercent() : 0.0,
data.isPartyOculus() ? data.getPartyOculusPercent()
: 0.0));
}
}), Dexterity("Dex", DamageAccumulator.Multiplicative,
"Dexterity bonus (1% damage per dex)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getDexterity() / 100.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.getDexterity() / 100.0;
}
}), CC("CC", DamageAccumulator.Special, "Chrit Chance bonus",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getCritChance();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.getCritChance();
}
}), CHD("CHD", DamageAccumulator.Special, "Chrit Hit Damage bonus",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getCritHitDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.getCritHitDamage();
}
}), OnCrit("OnCrit", DamageAccumulator.Special,
"On Crit Only Damage Bonus", new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
// TODO
return state.getData().getCritHitDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
// TODO
return 0.0;
}
}), Hysteria("Hysteria", DamageAccumulator.Additive,
"Scoundrel Hysteria Damage Bonus",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().isHysteria() ? 0.03 : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.isHysteria() ? 0.03 : 0.0;
}
}), OdysseysEnd("OE", DamageAccumulator.Additive,
"Odyssey's End Damage Bonus", new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getBuffs().isActive(Buff.OdysseysEnd) ? state
.getData().getOdysseysEndPercent() : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.isOdysseysEnd() ? data.getOdysseysEndPercent()
: 0.0;
}
}), EA("EA", DamageAccumulator.Additive,
"Elemental Arrow Skill Damage Bonus",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getEaDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.source.skill == ActiveSkill.EA) ? data
.getEaDamage() : 0.0;
}
}), CA("CA", DamageAccumulator.Additive,
"Cluster Arrow Skill Damage Bonus",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getCaDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.source.skill == ActiveSkill.CA) ? data
.getCaDamage() : 0.0;
}
}), MS("MS", DamageAccumulator.Additive,
"Multishot Skill Damage Bonus",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getMsDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.source.skill == ActiveSkill.MS) ? data
.getMsDamage() : 0.0;
}
}), IMP("Imp", DamageAccumulator.Additive,
"Impale Skill Damage Bonus", new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getImpDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.source.skill == ActiveSkill.IMP) ? data
.getImpDamage() : 0.0;
}
}), CHAK("Chak", DamageAccumulator.Additive,
"Chakram Skill Damage Bonus", new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getChakDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.source.skill == ActiveSkill.CHAK) ? data
.getChakDamage() : 0.0;
}
}), ILLWILL("IW", DamageAccumulator.Additive,
"Sword of Ill Will Chakram Bonus (1.0-1.4% per point of Hatred)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().isIllWill() ? (state.getData()
.getIllWillPercent() * state.getHatred()) : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return ((row.source.skill == ActiveSkill.CHAK) && data
.isIllWill()) ? (data.getIllWillPercent() * data
.getMaxHatred()) : 0.0;
}
}), HA("HA", DamageAccumulator.Additive,
"Hungering Arrow Skill Damage Bonus",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getHaDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.source.skill == ActiveSkill.HA) ? data
.getHaDamage() : 0.0;
}
}), Companion("Companion", DamageAccumulator.Additive,
"Companion Skill Damage Bonus",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getCompanionDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.source.skill == ActiveSkill.Companion) ? data
.getCompanionDamage() : 0.0;
}
}), Raven("Raven", DamageAccumulator.Multiplicative,
"Companion/Raven Active Damage Bonus",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getBuffs().isActive(Buff.CompanionActive) ? 5.0
: 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return 0.0;
}
}), ES("ES", DamageAccumulator.Additive,
"Entangling Shot Skill Damage Bonus",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getEsDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.source.skill == ActiveSkill.ES) ? data
.getEsDamage() : 0.0;
}
}), BOLAS("Bolas", DamageAccumulator.Additive,
"Bolas Skill Damage Bonus", new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getBolasDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.source.skill == ActiveSkill.BOLAS) ? data
.getBolasDamage() : 0.0;
}
}), EF("EF", DamageAccumulator.Additive,
"Evasive Fire Skill Damage Bonus",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getEfDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.source.skill == ActiveSkill.EF) ? data
.getEfDamage() : 0.0;
}
}), GRENADE("Grenade", DamageAccumulator.Additive,
"Grenade Skill Damage Bonus", new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getGrenadeDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.source.skill == ActiveSkill.GRENADE) ? data
.getGrenadeDamage() : 0.0;
}
}), ST("ST", DamageAccumulator.Additive, "Spike Trap Damage Bonus",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getSpikeTrapDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.source.skill == ActiveSkill.ST) ? data
.getSpikeTrapDamage() : 0.0;
}
}), FoK("FoK", DamageAccumulator.Additive,
"Fan of Knives Skill Damage Bonus",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getFokDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.source.skill == ActiveSkill.FoK) ? data
.getFokDamage() : 0.0;
}
}), Vengeance("Ven", DamageAccumulator.Additive,
"Vengeance Skill Damage Bonus",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getVengeanceDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.source.skill == ActiveSkill.Vengeance) ? data
.getVengeanceDamage() : 0.0;
}
}), Strafe("Strafe", DamageAccumulator.Additive,
"Strafe Skill Damage Bonus", new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getStrafeDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.source.skill == ActiveSkill.Strafe) ? data
.getStrafeDamage() : 0.0;
}
}), RF("RF", DamageAccumulator.Additive,
"Rapid Fire Skill Damage Bonus",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getRFDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.source.skill == ActiveSkill.RF) ? data
.getRFDamage() : 0.0;
}
}), RoV("RoV", DamageAccumulator.Additive,
"Rain of Vengeance Skill Damage Bonus",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().getRovDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.source.skill == ActiveSkill.RoV) ? data
.getRovDamage() : 0.0;
}
}), CtW("CtW", DamageAccumulator.Multiplicative,
"Cull the Weak passive bonus (20% to chilled/slowed)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().isCullTheWeak() ? (0.2 * state
.getData().getPercentSlowedChilled()) : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.isCullTheWeak() ? 0.2 : 0.0;
}
}), M6("M6", DamageAccumulator.Multiplicative,
"Marauder's 6 piece bonus (+1200% per Sentry)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return (state.getData().getNumMarauders() >= 6) ? (state
.getData().getNumSentries() * 12.0) : 0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (!sentry && (data.getNumMarauders() >= 6)) ? (data
.getNumSentries() * 12.0) : 0.0;
}
}), S2("S2", DamageAccumulator.Multiplicative,
"Shadow 2 piece bonus (+1200% if using Melee)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return ((state.getData().getNumShadow() >= 2)
&& (state.getData().getWeaponType() != null) && state
.getData().getWeaponType().isMelee()) ? 12.0 : 0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (!sentry
&& (row.source.skill != ActiveSkill.Companion)
&& (data.getNumShadow() >= 2)
&& (data.getWeaponType() != null) && data
.getWeaponType().isMelee()) ? 12.0 : 0.0;
}
}), S6(
"S6",
DamageAccumulator.Special,
"Shadow 6 piece Impale damage bonus (+40,000% to Impale's first target)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return (state.getData().getNumShadow() >= 6) ? 400.0 : 0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (!sentry && (row.source.skill == ActiveSkill.IMP) && (data
.getNumShadow() >= 6)) ? 400.0 : 0.0;
}
}), VenBuff("VenBuff", DamageAccumulator.Multiplicative,
"Vengeance damage buff (+40%)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getBuffs().isActive(Buff.Vengeance) ? 0.4 : 0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.getSkills().containsKey(ActiveSkill.Vengeance) ? 0.4
: 0.0;
}
}), LoN("LoN", DamageAccumulator.Multiplicative,
"Legacy of Nightmares buff (+100% per ancient item)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return ((state.getData().getNumLoN() >= 2)
&& (state.getData().getSetCounts().size() == 1) && !state
.getData().isOtherSets()) ? (1.0 * state.getData()
.getNumAncients()) : 0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return ((data.getNumLoN() >= 2)
&& (data.getSetCounts().size() == 1) && !data
.isOtherSets()) ? (1.0 * data.getNumAncients()) : 0;
}
}), N4("N4", DamageAccumulator.Multiplicative,
"Nat's 4 piece bonus (+100% to RoV)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return (state.getData().getNumNats() >= 4) ? 1.0 : 0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return ((row.source.skill == ActiveSkill.RoV) && (data
.getNumNats() >= 4)) ? 1.0 : 0.0;
}
}), RoVN6(
"N6RoV",
DamageAccumulator.Multiplicative,
"Nat's 6 piece bonus to RoV (+500% damage for 10 seconds after RoV)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getBuffs().isActive(Buff.N6) ? 5.0 : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return ((row.source.skill == ActiveSkill.RoV) && (data
.getNumNats() >= 6)) ? 5.0 : 0.0;
}
}), N6(
"N6",
DamageAccumulator.Multiplicative,
"Nat's 6 piece bonus to other skills(+500% damage for 5 seconds after RoV)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
// TODO do we need to calculate uptime?
if (state.getData().getNumNats() < 6)
return 0.0;
double interval = (1.0 / state.getData().getAps())
+ (state.getData().getDelay() / 1000.0);
double cdr = state.getData().getCdr();
double rovCD = 30.0 * (1.0 - cdr);
double numAttacks = rovCD / (interval + 2.0);
rovCD = numAttacks * interval;
if (rovCD <= 5.0)
return 5.0;
else
return Math.round(500.0 * (5.0 / rovCD)) / 100.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return ((row.source.skill != ActiveSkill.RoV) && (data
.getNumNats() >= 6)) ? 5.0 : 0.0;
}
}), Elite("Elite", DamageAccumulator.Multiplicative,
"Elite damage bonus (includes BotP if rank 25+)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
// TODO Apply Target Type
return state.getData().getTotalEliteDamage();
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.getTotalEliteDamage();
}
}), DoT("DoT", DamageAccumulator.Multiplicative,
"Damage over Time (damage per second, not per attack)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return 0.0;
}
}), BoT("BotT", DamageAccumulator.Multiplicative,
"Bane of the Trapped bonus (15% + 3%/rank to control impaired)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().isUseBaneOfTheTrapped() ? ((0.15 + (state
.getData().getBaneOfTheTrappedLevel() * 0.003)) * state
.getData().getPercentControlled())
: 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.isUseBaneOfTheTrapped() ? (0.15 + (data
.getBaneOfTheTrappedLevel() * 0.003)) : 0.0;
}
}), BotP("BotP", DamageAccumulator.Multiplicative,
"Bane of the Powerful active gem bonus (20% while active)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getBuffs().isActive(Buff.BotP) ? 0.2 : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.isBotp() ? 0.2 : 0.0;
}
}), BotS(
"BotS",
DamageAccumulator.Multiplicative,
"Bane of the Stricken gem per stack bonus (0.80% + 0.01 % per level, per stack)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return (state.getData().isBotS() && (state.getData()
.getTargetType() == TargetType.Primary)) ? ((0.008 + (state
.getData().getBotSLevel() * 0.0001)) * state
.getTargets().getTarget(TargetType.Primary)
.getBotsStacks()) : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.isBotS() ? ((0.008 + (data.getBotSLevel() * 0.0001)) * 100.0)
: 0.0;
}
}), BotS25(
"BotS25",
DamageAccumulator.Multiplicative,
"Bane of the Stricken rank 25 bonus (25% against bosses and rift guardians)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return (state.getData().isBotS()
&& (state.getData().getBotSLevel() >= 25)
&& (state.getData().getPrimaryTargetType() == MonsterType.RiftGuardian) && (state
.getData().getTargetType() == TargetType.Primary)) ? 0.25
: 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (data.isBotS() && (data.getBotSLevel() >= 25)) ? 0.25
: 0.0;
}
}), IAS("IAS", DamageAccumulator.Multiplicative,
"Character IAS bonus for Companions",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return (state.getData().getEquipIas()
+ (state.getData().getParagonIAS() * 0.002)
+ (state.getData().isGogok() ? (state.getData()
.getGogokStacks() * 0.01) : 0.0)
+ (state.getData().isFocusedMind() ? 0.03 : 0.0) + (state
.getData().isRetribution() ? (0.1 * state.getData()
.getRetributionUptime()) : 0.0));
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (row.source.skill == ActiveSkill.Companion) ? (data
.getEquipIas()
+ (data.getParagonIAS() * 0.002)
+ (data.isGogok() ? 0.15 : 0.0)
+ (data.isFocusedMind() ? 0.03 : 0.0) + (data
.isRetribution() ? 0.1 : 0.0)) : 0.0;
}
}), APS("APS", DamageAccumulator.Multiplicative,
"Weapon APS bonus for Companions",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
WeaponType type = state.getData().getWeaponType();
if (type == null)
type = WeaponType.Bow;
return (type.getAps() * (1.0 + state.getData()
.getWeaponIas())) - 1.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
if (row.source.skill != ActiveSkill.Companion)
return 0.0;
WeaponType type = data.getWeaponType();
if (type == null)
type = WeaponType.Bow;
return (type.getAps() * (1.0 + data.getWeaponIas())) - 1.0;
}
}), CaltropsBT(
"Caltrops",
DamageAccumulator.Special,
"Caltrops/Bait the Trap active bonus (10% Crit Chance while active)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getBuffs().isActive(Buff.Caltrops) ? 0.1 : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (data.getSkills().get(ActiveSkill.Caltrops) == Rune.Bait_the_Trap) ? 0.1
: 0.0;
}
}), Taeguk("Taeguk", DamageAccumulator.Multiplicative,
"Taeguk active gem bonus (2% + 0.04% per stack)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().isTaeguk() ? ((0.02 + (0.0004 * state
.getData().getTaegukLevel())) * state.getData()
.getTaegukStacks()) : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return ((data.getSkills().containsKey(ActiveSkill.Strafe) || (row.source.skill == ActiveSkill.RF)) && data
.isTaeguk()) ? ((0.02 + (0.0004 * data
.getTaegukLevel())) * 10.0) : 0.0;
}
}), Simplicity(
"Simplicity",
DamageAccumulator.Multiplicative,
"Simplicity's Strenght gem bonus (25% + 0.5 % per level to Primary skills)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getData().isSimplicity() ? (0.25 + (state
.getData().getSimplicityLevel() * .005)) : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return ((row.source.skill.getSkillType() == SkillType.Primary) && data
.isSimplicity()) ? (0.25 + (data
.getSimplicityLevel() * .005)) : 0.0;
}
}), Wolf("Wolf", DamageAccumulator.Multiplicative,
"Wolf Companion active bonus (15% during uptime)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return (state.getBuffs().isActive(Buff.Wolf) || state
.getBuffs().isActive(Buff.OtherWolf)) ? 0.15 : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return ((data.getSkills()
.containsKey(ActiveSkill.Companion) && ((data
.getNumMarauders() >= 2) || (data.getSkills().get(
ActiveSkill.Companion) == Rune.Wolf))) ||
data.isWolf()) ? 0.15 : 0.0;
}
}), Bbv("Bbv", DamageAccumulator.Additive,
"Big Bad Voodoo/Slam Dance active bonus (30% during uptime)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getBuffs().isActive(Buff.Bbv) ? 0.3 : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.isBbv() ? 0.3 : 0.0;
}
}), TW("TW", DamageAccumulator.Additive,
"Slow Time/Time Warp (15% during uptime)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getBuffs().isActive(Buff.TimeWarp) ? 0.15
: 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.isTimeWarp() ? 0.15 : 0.0;
}
}), Piranhas("Piranhas", DamageAccumulator.Additive,
"Piranhas active bonus (15% during uptime)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getBuffs().isActive(Buff.Piranhas) ? 0.15
: 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.isPiranhas() ? 0.15 : 0.0;
}
}), InnerSanctuary(
"InnerSanctuary",
DamageAccumulator.Additive,
"Inner Sanctuary/Forbidden Palace active bonus (30% during uptime)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getBuffs().isActive(Buff.InnerSanct) ? 0.3
: 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.isInnerSanctuary() ? 0.3 : 0.0;
}
}), CripplingWave("CripplingWave", DamageAccumulator.Additive,
"Crippling Wave/Breaking Wave active bonus (10% during uptime)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getBuffs().isActive(Buff.CripWave) ? 0.1 : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.isCripplingWave() ? 0.1 : 0.0;
}
}), Conviction("Conviction", DamageAccumulator.Additive,
"Conviction bonus (10-20% during uptime)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
if (state.getBuffs().isActive(Buff.ConvictionActive)) {
return 0.2;
} else if (state.getBuffs()
.isActive(Buff.ConvictionPassive)) {
return state.getData().isOverawe() ? 0.16 : 0.10;
} else {
return 0.0;
}
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.isConviction() ? 0.2 : 0.0;
}
}), Paranoia("Paranoia", DamageAccumulator.Additive,
"Mass Confusion/Paranoia active bonus (20% during uptime)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return state.getBuffs().isActive(Buff.Paranoia) ? 0.2 : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.isMassConfusion() ? 0.2 : 0.0;
}
}), MfD("MfD", DamageAccumulator.Additive,
"Marked for Death active skill bonus (15%)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
TargetType type = state.getData().getTargetType();
if (type.isPrimary()
&& !state.getBuffs().isActive(Buff.MfdPrimary))
return 0.0;
if (!type.isPrimary()
&& !state.getBuffs().isActive(Buff.MfdAdditional))
return 0.0;
double scalar = 0.15;
double aoe = 0.0;
// TODO fix
if ((state.getData().getMfdRune() == Rune.Grim_Reaper)
&& (state.getData().getNumAdditional() > 0)
&& (state.getData().getTargetSpacing() <= 20)) {
aoe = (0.15 / state.getData().getNumAdditional());
}
return scalar + aoe;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
if ((data.getMfdRune() == Rune.Grim_Reaper)
&& (data.getNumAdditional() > 0)
&& (data.getTargetSpacing() <= 20))
return 0.3;
else
return data.isMarked() ? 0.15 : 0.0;
}
}), Vaxo("Vaxo", DamageAccumulator.Additive,
"Haunt of Vaxo Marked for Death bonus (15% during uptime)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
TargetType type = state.getData().getTargetType();
if (type == TargetType.Primary) {
return (state.getBuffs().isActive(Buff.Vaxo)
&& !state.getBuffs().isActive(Buff.Calamity) && !state
.getBuffs().isActive(Buff.MfdPrimary)) ? 0.15
: 0.0;
} else {
return (state.getBuffs().isActive(Buff.Vaxo)
&& !state.getBuffs().isActive(Buff.Calamity)
&& !state.getBuffs().isActive(
Buff.MfdAdditional) && (state.getData()
.getTargetSpacing() <= 15)) ? 0.15 : 0.0;
}
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (data.isVaxo() && !data.isCalamityMdf() && !data
.getSkills().containsKey(ActiveSkill.MFD)) ? 0.15
: 0.0;
}
}), AD("Area", DamageAccumulator.Special,
"Area Damage (20% chance)", new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return ((state.getTargets().getNumAlive() > 1) && (state
.getData().getTargetSpacing() <= 10)) ? state
.getData().getAreaDamage() : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return ((data.getNumAdditional() > 0) && (data
.getTargetSpacing() <= 10)) ? (data.getAreaDamage() * data
.getNumAdditional()) : 0.0;
}
}), Calamity("Calamity", DamageAccumulator.Additive,
"Calamity Marked for Death bonus (15% while applied)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
TargetType type = state.getData().getTargetType();
Rune rune = Rune.None;
if (state.getData().isMarked()) {
rune = state.getData().getMfdRune();
}
double scalar = 0.15;
double aoe = 0.0;
if ((rune == Rune.Grim_Reaper)
&& (state.getData().getNumAdditional() > 0)
&& (state.getData().getTargetSpacing() <= 20)) {
aoe = (0.15 / state.getData().getNumAdditional());
}
if (state.getBuffs().isActive(Buff.Calamity)
&& !state.getBuffs().isActive(Buff.Vaxo)) {
if (type == TargetType.Primary) {
return !state.getBuffs().isActive(Buff.MfdPrimary) ? (scalar + aoe)
: 0.0;
} else {
return !state.getBuffs().isActive(
Buff.MfdAdditional) ? (scalar + aoe) : 0.0;
}
} else {
return 0.0;
}
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (data.isCalamityMdf() && !data.getSkills()
.containsKey(ActiveSkill.MFD)) ? 0.15 : 0.0;
}
}), Toxicity("Toxicity", DamageAccumulator.Additive,
"Gem of Efficacious Toxin rank 25+ bonus (10%)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return (state.getData().isToxin() && state.getData()
.getToxinLevel() >= 25) ? 0.1 : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (data.isToxin() && data.getToxinLevel() >= 25) ? 0.1
: 0.0;
}
}), Ambush("Ambush", DamageAccumulator.Multiplicative,
"Ambush (40% to enemies above 75% health)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
TargetType type = state.getData().getTargetType();
return (state.getData().isAmbush() && state.getTargets()
.getTarget(type).getPercentHealth() >= 0.75) ? 0.4
: 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.isAmbush() ? 0.4 : 0.0;
}
}), Iced(
"Iced",
DamageAccumulator.Special,
"Iceblink rank 25 bonus (10% Crit Chance to enemies chilled/frozen)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return (state.getData().isIceblink() && (state.getData()
.getIceblinkLevel() >= 25)) ? (0.1 * state
.getData().getPercentSlowedChilled()) : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (data.isIceblink() && (data.getIceblinkLevel() >= 25)) ? 0.1
: 0.0;
}
}), DML(
"DML",
DamageAccumulator.Special,
"Dead Man's Legacy (Multishot hits twice when target is below 50-60% health)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return data.isDml() ? 1.0 : 0.0;
}
}), SingleOut(
"SingleOut",
DamageAccumulator.Special,
"Single Out (25% Crit Chance to enemies more than 20 yards away from other enemies)",
new Test<SimulationState, Double>() {
@Override
public Double getValue(SimulationState state) {
return (state.getData().isSingleOut() && (state.getData()
.getTargetSpacing() >= 20)) ? 0.25 : 0.0;
}
@Override
public Double getMax(boolean sentry, DamageRow row,
CharacterData data) {
return (data.isSingleOut() && (data.getTargetSpacing() >= 20)) ? 0.25
: 0.0;
}
});
private String abbrev;
private Test<SimulationState, Double> test;
private DamageAccumulator accumulator;
private String description;
private DamageMultiplier(String abbrev, DamageAccumulator accumulator,
String description, Test<SimulationState, Double> test) {
this.abbrev = abbrev;
this.test = test;
this.accumulator = accumulator;
this.description = description;
}
public DamageAccumulator getAccumulator() {
return this.accumulator;
}
public String getDescription() {
return this.description;
}
public String getAbbreviation() {
return abbrev;
}
public boolean hasTest() {
return test != null;
}
public double getValue(SimulationState state) {
return test.getValue(state);
}
public double getMax(boolean sentry, DamageRow row, CharacterData data) {
return test.getMax(sentry, row, data);
}
}