<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.seculargames.org/index.php?action=history&amp;feed=atom&amp;title=MediaWiki%3ADamageCalculator.js</id>
	<title>MediaWiki:DamageCalculator.js - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.seculargames.org/index.php?action=history&amp;feed=atom&amp;title=MediaWiki%3ADamageCalculator.js"/>
	<link rel="alternate" type="text/html" href="https://wiki.seculargames.org/index.php?title=MediaWiki:DamageCalculator.js&amp;action=history"/>
	<updated>2026-07-29T05:22:18Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.45.3</generator>
	<entry>
		<id>https://wiki.seculargames.org/index.php?title=MediaWiki:DamageCalculator.js&amp;diff=45797&amp;oldid=prev</id>
		<title>imported&gt;Loramin at 23:33, 13 April 2025</title>
		<link rel="alternate" type="text/html" href="https://wiki.seculargames.org/index.php?title=MediaWiki:DamageCalculator.js&amp;diff=45797&amp;oldid=prev"/>
		<updated>2025-04-13T23:33:59Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;// Wiki form&lt;br /&gt;
&lt;br /&gt;
const fieldLabels = {&lt;br /&gt;
    &amp;#039;level&amp;#039;: &amp;#039;Level&amp;#039;,&lt;br /&gt;
    &amp;#039;strength&amp;#039;: &amp;#039;Strength&amp;#039;,&lt;br /&gt;
    &amp;#039;offenseSkill&amp;#039;: &amp;#039;Offense Skill&amp;#039;,&lt;br /&gt;
    &amp;#039;doubleAttackSkill&amp;#039;: &amp;#039;Double Attack Skill&amp;#039;,&lt;br /&gt;
    &amp;#039;dualWieldSkill&amp;#039;: &amp;#039;Dual Wield Skill&amp;#039;,&lt;br /&gt;
    &amp;#039;hastePercentage&amp;#039;: &amp;#039;Haste Percentage&amp;#039;,&lt;br /&gt;
    &amp;#039;itemAndSpellATKTotal&amp;#039;: &amp;#039;Item and Spell ATK Total&amp;#039;,&lt;br /&gt;
    &amp;#039;mainHandWeaponSkill&amp;#039;: &amp;#039;Main Hand Weapon Skill&amp;#039;,&lt;br /&gt;
    &amp;#039;mainHandWeaponDamage&amp;#039;: &amp;#039;Main Hand Weapon Damage&amp;#039;,&lt;br /&gt;
    &amp;#039;mainHandWeaponDelay&amp;#039;: &amp;#039;Main Hand Weapon Delay&amp;#039;,&lt;br /&gt;
    &amp;#039;mainHandDamageBonus&amp;#039;: &amp;#039;Main Hand Damage Bonus&amp;#039;,&lt;br /&gt;
    &amp;#039;offHandWeaponSkill&amp;#039;: &amp;#039;Off Hand Weapon Skill&amp;#039;,&lt;br /&gt;
    &amp;#039;offHandWeaponDamage&amp;#039;: &amp;#039;Off Hand Weapon Damage&amp;#039;,&lt;br /&gt;
    &amp;#039;offHandWeaponDelay&amp;#039;: &amp;#039;Off Hand Weapon Delay&amp;#039;,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
const fieldDefaults = {&lt;br /&gt;
    &amp;#039;level&amp;#039;: 52,&lt;br /&gt;
    &amp;#039;strength&amp;#039;: 144,&lt;br /&gt;
    &amp;#039;offenseSkill&amp;#039;: 236,&lt;br /&gt;
    &amp;#039;doubleAttackSkill&amp;#039;: 200,&lt;br /&gt;
    &amp;#039;dualWieldSkill&amp;#039;: 252,&lt;br /&gt;
    &amp;#039;hastePercentage&amp;#039;: 34,&lt;br /&gt;
    &amp;#039;itemAndSpellATKTotal&amp;#039;: 0,&lt;br /&gt;
    &amp;#039;mainHandWeaponSkill&amp;#039;: 236,&lt;br /&gt;
    &amp;#039;mainHandWeaponDamage&amp;#039;: 9,&lt;br /&gt;
    &amp;#039;mainHandWeaponDelay&amp;#039;: 16,&lt;br /&gt;
    &amp;#039;mainHandDamageBonus&amp;#039;: 9,&lt;br /&gt;
    &amp;#039;offHandWeaponSkill&amp;#039;: 236,&lt;br /&gt;
    &amp;#039;offHandWeaponDamage&amp;#039;: 17,&lt;br /&gt;
    &amp;#039;offHandWeaponDelay&amp;#039;: 28&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
const fields = Object.keys(fieldLabels);&lt;br /&gt;
&lt;br /&gt;
const fieldsHtml = fields.map(field =&amp;gt;&lt;br /&gt;
    `&amp;lt;label&amp;gt;${fieldLabels[field]}: &amp;lt;input name=&amp;quot;${field}&amp;quot; placeholder=&amp;quot;${fieldDefaults[field]}&amp;quot;/&amp;gt;&amp;lt;label&amp;gt;`&lt;br /&gt;
).join(&amp;#039;&amp;lt;br/&amp;gt;&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
const mobFieldLabels = {&lt;br /&gt;
    agility: &amp;#039;Mob Agility&amp;#039;,&lt;br /&gt;
    defenseSkill: &amp;#039;Mob Defense Skill&amp;#039;,&lt;br /&gt;
    totalAC: &amp;#039;Mob Total AC&amp;#039;,&lt;br /&gt;
    riposteSkill: &amp;#039;Mob Riposte Skill&amp;#039;,&lt;br /&gt;
    parrySkill: &amp;#039;Mob Parry Skill&amp;#039;,&lt;br /&gt;
    dodgeSkill: &amp;#039;Mob Dodge Skill&amp;#039;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const mobFieldDefaults = {&lt;br /&gt;
    agility: 150,&lt;br /&gt;
    defenseSkill: 200,&lt;br /&gt;
    totalAC: 115,&lt;br /&gt;
    riposteSkill: 25,&lt;br /&gt;
    parrySkill: 25,&lt;br /&gt;
    dodgeSkill: 25&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
const mobFields = Object.keys(mobFieldLabels);&lt;br /&gt;
&lt;br /&gt;
const mobFieldsHtml = mobFields.map(field =&amp;gt;&lt;br /&gt;
    `&amp;lt;label&amp;gt;${mobFieldLabels[field]}: &amp;lt;input name=&amp;quot;${field}&amp;quot; placeholder=&amp;quot;${mobFieldDefaults[field]}&amp;quot;/&amp;gt;&amp;lt;label&amp;gt;`&lt;br /&gt;
).join(&amp;#039;&amp;lt;br/&amp;gt;&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
var playerStats = createStatStruct();&lt;br /&gt;
//playerStats.strength = 144;&lt;br /&gt;
//playerStats.offenseSkill = 236;&lt;br /&gt;
//playerStats.doubleAttackSkill = 200;&lt;br /&gt;
//playerStats.dualWieldSkill = 252;&lt;br /&gt;
//playerStats.hastePercentage = 34;&lt;br /&gt;
//playerStats.itemAndSpellATKTotal = 0;&lt;br /&gt;
//playerStats.mainHandWeaponSkill = 236;&lt;br /&gt;
//playerStats.mainHandWeaponDamage = 9;&lt;br /&gt;
//playerStats.mainHandWeaponDelay = 16;&lt;br /&gt;
//playerStats.mainHandDamageBonus = 9;&lt;br /&gt;
//playerStats.offHandWeaponSkill = 236;&lt;br /&gt;
//playerStats.offHandWeaponDamage = 17;&lt;br /&gt;
//playerStats.offHandWeaponDelay = 28;&lt;br /&gt;
&lt;br /&gt;
//================================&lt;br /&gt;
// Edit these variables&lt;br /&gt;
//================================&lt;br /&gt;
&lt;br /&gt;
playerStats.strength = 144;&lt;br /&gt;
playerStats.offenseSkill = 236;&lt;br /&gt;
playerStats.doubleAttackSkill = 200;&lt;br /&gt;
playerStats.dualWieldSkill = 252;&lt;br /&gt;
playerStats.hastePercentage = 34;&lt;br /&gt;
playerStats.itemAndSpellATKTotal = 0;&lt;br /&gt;
playerStats.mainHandWeaponSkill = 236;&lt;br /&gt;
playerStats.mainHandWeaponDamage = 9;&lt;br /&gt;
playerStats.mainHandWeaponDelay = 16;&lt;br /&gt;
playerStats.mainHandDamageBonus = 9;&lt;br /&gt;
playerStats.offHandWeaponSkill = 236;&lt;br /&gt;
playerStats.offHandWeaponDamage = 17;&lt;br /&gt;
playerStats.offHandWeaponDelay = 28;&lt;br /&gt;
&lt;br /&gt;
var mobStats = createStatStruct();&lt;br /&gt;
mobStats.agility = 150; // AGI doesn&amp;#039;t have a large effect, except at low levels. Setting it somewhere above 75 is fine. &lt;br /&gt;
mobStats.defenseSkill = 200; // Rule of thumb is probably 5 + (5 * level), just like player melee classes.&lt;br /&gt;
mobStats.totalAC = 115; // Level 50 mobs supposedly have around 115 AC according to EQEMU comments. Assume lower levels have lower AC. Raid targets probably have a decent amount more.&lt;br /&gt;
mobStats.riposteSkill = 25; // For now rule of thumb is Level / 2. Based on P99 parsing riposte amount doesn&amp;#039;t match skill level of 5 + (5 * level), or magic numbers are off in EQEMU code.&lt;br /&gt;
mobStats.parrySkill = 25; // For now rule of thumb is Level / 2. Based on P99 parsing parry amount doesn&amp;#039;t match skill level of 5 + (5 * level), or magic numbers are off in EQEMU code.&lt;br /&gt;
mobStats.dodgeSkill = 25; // For now rule of thumb is Level / 2. Based on P99 parsing dodge amount doesn&amp;#039;t match skill level of 5 + (5 * level), or magic numbers are off in EQEMU code.&lt;br /&gt;
&lt;br /&gt;
//===============================&lt;br /&gt;
// Do not edit, this is the code&lt;br /&gt;
//===============================&lt;br /&gt;
&lt;br /&gt;
//--------------------&lt;br /&gt;
// GENERAL CODE NOTES:&lt;br /&gt;
//--------------------&lt;br /&gt;
// varuables with prefix mn_ are magic numbers ripped directly from EQEMU code.&lt;br /&gt;
// Math.random() generates a value between 0.0 and 1.0.&lt;br /&gt;
&lt;br /&gt;
//console.log(GetDamageTable(playerStats.level));&lt;br /&gt;
//console.log(&amp;quot;ComputeToHit() = &amp;quot; + ComputeToHit(playerStats, false));&lt;br /&gt;
//console.log(&amp;quot;ComputeDefense() = &amp;quot; + ComputeDefense(mobStats));&lt;br /&gt;
//console.log(&amp;quot;ComputeMitigation() = &amp;quot; + ComputeMitigation(mobStats));&lt;br /&gt;
//console.log(&amp;quot;ComputeWrath() = &amp;quot; + ComputeWrath(playerStats, false));&lt;br /&gt;
//console.log(&amp;quot;RollD20() = &amp;quot; + RollD20(ComputeWrath(playerStats, false), ComputeMitigation(mobStats)));&lt;br /&gt;
//console.log(&amp;quot;ApplyDamageTable() with Damage Bonus = &amp;quot; + (ApplyDamageTable(playerStats.mainHandWeaponDamage * RollD20(ComputeWrath(playerStats, false), ComputeMitigation(mobStats)), playerStats, { bForceMaxDamageInput: false, bUseOffhand: false })));&lt;br /&gt;
&lt;br /&gt;
const makeTable = results =&amp;gt; &lt;br /&gt;
    `&amp;lt;table&amp;gt;`+&lt;br /&gt;
    `&amp;lt;thead&amp;gt;&amp;lt;tr&amp;gt;${results.map((_, i) =&amp;gt; `&amp;lt;th style=&amp;quot;border: 1px solid black; padding: 0.2em&amp;quot;&amp;gt;${i ? i : &amp;#039;Misses&amp;#039;}&amp;lt;/td&amp;gt;`).join(&amp;#039;&amp;#039;)}&amp;lt;/th&amp;gt;&amp;lt;/thead&amp;gt;`+&lt;br /&gt;
    `&amp;lt;tbody&amp;gt;&amp;lt;tr&amp;gt;${results.map(result =&amp;gt; `&amp;lt;td style=&amp;quot;border: 1px solid black; padding: 0.2em&amp;quot;&amp;gt;${result}&amp;lt;/td&amp;gt;`).join(&amp;#039;&amp;#039;)}&amp;lt;/tr&amp;gt;&amp;lt;/tbody&amp;gt;`+&lt;br /&gt;
    `&amp;lt;/table&amp;gt;`;&lt;br /&gt;
&lt;br /&gt;
const displayResults = () =&amp;gt; {&lt;br /&gt;
    for (const field of fields) {&lt;br /&gt;
        playerStats[field] = Number($(`[name=&amp;quot;${field}&amp;quot;]`).val() || $(`[name=&amp;quot;${field}&amp;quot;]`).attr(&amp;#039;placeholder&amp;#039;));&lt;br /&gt;
        if (!playerStats[field] &amp;amp;&amp;amp; playerStats[field] !== 0) {&lt;br /&gt;
            alert(`You must supply a ${fieldLabels[field]}!`);&lt;br /&gt;
            return false;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
    for (const field of mobFields) {&lt;br /&gt;
        mobStats[field] = Number($(`[name=&amp;quot;${field}&amp;quot;]`).val() || $(`[name=&amp;quot;${field}&amp;quot;]`).attr(&amp;#039;placeholder&amp;#039;));&lt;br /&gt;
        if (!mobStats[field] &amp;amp;&amp;amp; mobStats[field] !== 0) {&lt;br /&gt;
            alert(`You must supply a ${fieldLabels[field]}!`);&lt;br /&gt;
            return false;&lt;br /&gt;
        }&lt;br /&gt;
    };&lt;br /&gt;
    const results = RunDPSTest(playerStats, mobStats, 500);&lt;br /&gt;
    $(&amp;#039;#results&amp;#039;).html(&lt;br /&gt;
      Object.keys(results)&lt;br /&gt;
        .map(label =&amp;gt; {&lt;br /&gt;
          const value = Array.isArray(results[label]) ? makeTable(results[label]): results[label];&lt;br /&gt;
          return `&amp;lt;div&amp;gt;${label}: ${value}&amp;lt;/div&amp;gt;`;&lt;br /&gt;
        })&lt;br /&gt;
        .join(&amp;#039;&amp;#039;)&lt;br /&gt;
    );&lt;br /&gt;
    return false;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$(&amp;#039;#form&amp;#039;).html(`&lt;br /&gt;
    &amp;lt;form&amp;gt;&lt;br /&gt;
    &amp;lt;fieldset&amp;gt;${fieldsHtml}&amp;lt;/fieldset&amp;gt;&lt;br /&gt;
    &amp;lt;fieldset&amp;gt;${mobFieldsHtml}&amp;lt;/fieldset&amp;gt;&lt;br /&gt;
    &amp;lt;button type=&amp;quot;submit&amp;quot;&amp;gt;Submit&amp;lt;/button&amp;gt;&lt;br /&gt;
    &amp;lt;/form&amp;gt;&lt;br /&gt;
    &amp;lt;div id=&amp;quot;results&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  `);&lt;br /&gt;
$(&amp;#039;#form form&amp;#039;).submit(displayResults);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function createStatStruct() {&lt;br /&gt;
    var newStatStruct = {&lt;br /&gt;
        level: 0,&lt;br /&gt;
&lt;br /&gt;
        // Player Only for now (don&amp;#039;t have mob DPS set up)&lt;br /&gt;
        strength: 0,&lt;br /&gt;
        offenseSkill: 0,&lt;br /&gt;
        doubleAttackSkill: 0,&lt;br /&gt;
        dualWieldSkill: 0,&lt;br /&gt;
        hastePercentage: 0,&lt;br /&gt;
        itemAndSpellATKTotal: 0,&lt;br /&gt;
        mainHandWeaponSkill: 0,&lt;br /&gt;
        mainHandWeaponDamage: 0,&lt;br /&gt;
        mainHandWeaponDelay: 0,&lt;br /&gt;
        mainHandDamageBonus: 0,&lt;br /&gt;
        offHandWeaponSkill: 0,&lt;br /&gt;
        offHandWeaponDamage: 0,&lt;br /&gt;
        offHandWeaponDelay: 0,&lt;br /&gt;
&lt;br /&gt;
        // Mob Only for now (don&amp;#039;t have mob DPS set up)&lt;br /&gt;
        agility: 0, // AGI doesn&amp;#039;t have a large effect. Setting it somewhere above 75 is fine.&lt;br /&gt;
        defenseSkill: 0,  // Rule of thumb is probably 5 + (5 * level), just like player melee classes.&lt;br /&gt;
        totalAC: 0, // Level 50 mobs supposedly have around 115 AC according to EQEMU comments. Assume lower levels have lower AC. Raid targets probably have a decent amount more.&lt;br /&gt;
        riposteSkill: 0, // Rule of thumb is probably 5 + (5 * level), just like player melee classes.&lt;br /&gt;
        parrySkill: 0, // Rule of thumb is probably 5 + (5 * level), just like player melee classes.&lt;br /&gt;
        dodgeSkill: 0, // Rule of thumb is probably 5 + (5 * level), just like player melee classes.&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    return newStatStruct;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function GetDamageTable(levelInput) {&lt;br /&gt;
    const mn_DamageTableArray = [&lt;br /&gt;
        { maxExtra: 210, chance: 49, minusFactor: 105 }, // level 1-50&lt;br /&gt;
        { maxExtra: 245, chance: 35, minusFactor: 80 }, // level 51-54&lt;br /&gt;
        { maxExtra: 265, chance: 28, minusFactor: 70 }, // level 55-59&lt;br /&gt;
        { maxExtra: 285, chance: 23, minusFactor: 65 } // level 60&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    if (levelInput &amp;gt; 50 &amp;amp;&amp;amp; levelInput &amp;lt;= 54) {&lt;br /&gt;
        return mn_DamageTableArray[1];&lt;br /&gt;
    }&lt;br /&gt;
    else if (levelInput &amp;gt;= 55 &amp;amp;&amp;amp; levelInput &amp;lt; 60) {&lt;br /&gt;
        return mn_DamageTableArray[2];&lt;br /&gt;
    }&lt;br /&gt;
    else if (levelInput &amp;gt;= 60) {&lt;br /&gt;
        return mn_DamageTableArray[3];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return mn_DamageTableArray[0];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function ComputeToHit(attackerStatStruct, bUseOffhand) {&lt;br /&gt;
    var attackerWeaponSkill = attackerStatStruct.mainHandWeaponSkill;&lt;br /&gt;
    if (bUseOffhand) {&lt;br /&gt;
        attackerWeaponSkill = attackerStatStruct.offHandWeaponSkill;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    const mn_SkillBonus = 7 + 10; // There are two different magic numbers in the code for this, which is why I keep them separate for reference.&lt;br /&gt;
    const mn_SkillMultiplier = 121;&lt;br /&gt;
    const mn_SkillDenominator = 100;&lt;br /&gt;
&lt;br /&gt;
    return ((attackerStatStruct.offenseSkill + attackerWeaponSkill + mn_SkillBonus) * mn_SkillMultiplier) / mn_SkillDenominator;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function ComputeDefense(defenderStatStruct) {&lt;br /&gt;
    // These two variables are designed to adjust AGI effectivenes based on era.&lt;br /&gt;
    const mn_AgilityEraMultiplier = 1000; // May need to adjust if P99 specifically uses a different era offset.&lt;br /&gt;
    const mn_AgilityEraDenominator = 1000; // Don&amp;#039;t change this variable. The EraMultiplier needs this to stay at 1000.&lt;br /&gt;
&lt;br /&gt;
    const mn_DefenseSkillBonus = 10;&lt;br /&gt;
    const mn_DefenseSkillMultiplier = 400;&lt;br /&gt;
    const mn_DefenseSkillDenomimator = 225;&lt;br /&gt;
    const mn_AgilitySubtraction = 40;&lt;br /&gt;
    const mn_AgilityMultiplier = 800;&lt;br /&gt;
    const mn_AgilityDenominator = 3600;&lt;br /&gt;
&lt;br /&gt;
    const finalDefense = ((defenderStatStruct.defenseSkill * mn_DefenseSkillMultiplier) / mn_DefenseSkillDenomimator) + mn_DefenseSkillBonus;&lt;br /&gt;
    const finalAgility = (mn_AgilityEraMultiplier * (mn_AgilityMultiplier * (defenderStatStruct.agility - mn_AgilitySubtraction)) / mn_AgilityDenominator) / mn_AgilityEraDenominator;&lt;br /&gt;
&lt;br /&gt;
    return finalDefense + finalAgility;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function ComputeMitigation(defenderStatStruct) {&lt;br /&gt;
    const mn_DefenseSkillDenominator = 5;&lt;br /&gt;
    const mn_AgilityDenominator = 20;&lt;br /&gt;
&lt;br /&gt;
    return defenderStatStruct.totalAC + (defenderStatStruct.defenseSkill / mn_DefenseSkillDenominator) + (defenderStatStruct.agility / mn_AgilityDenominator);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// This is normally called &amp;quot;offense&amp;quot; in the EQEMU code (not the same as the Offense Skill). Renaming it to &amp;quot;wrath&amp;quot; (I made this up) to reduce confusion.&lt;br /&gt;
function ComputeWrath(attackerStatStruct, bUseOffhand) {&lt;br /&gt;
    var attackerWeaponSkill = attackerStatStruct.mainHandWeaponSkill;&lt;br /&gt;
    if (bUseOffhand) {&lt;br /&gt;
        attackerWeaponSkill = attackerStatStruct.offHandWeaponSkill;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // These two variables are designed to adjust ATK effectivenes based on era.&lt;br /&gt;
    const mn_ATKEraMultiplier = 100; // May need to adjust if P99 specifically uses a different era offset.&lt;br /&gt;
    const mn_ATKEraDenominator = 100; // Don&amp;#039;t change this variable. The EraMultiplier needs this to stay at 100.&lt;br /&gt;
&lt;br /&gt;
    const mn_ATKDenominator = 2;&lt;br /&gt;
    const mn_minStrength = 75;&lt;br /&gt;
    const mn_strengthMultiplier = 2;&lt;br /&gt;
    const mn_strengthSubtraction = 150;&lt;br /&gt;
    const mn_strengthDenominator = 3;&lt;br /&gt;
&lt;br /&gt;
    var StrengthModifier = 0;&lt;br /&gt;
    if (attackerStatStruct.strength &amp;gt;= mn_minStrength) {&lt;br /&gt;
        StrengthModifier = ((mn_strengthMultiplier * attackerStatStruct.strength) - mn_strengthSubtraction) / mn_strengthDenominator;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return attackerWeaponSkill + StrengthModifier + ((attackerStatStruct.itemAndSpellATKTotal / mn_ATKDenominator) * mn_ATKEraMultiplier) / mn_ATKEraDenominator;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function RollD20(wrathInput, mitigationInput) {&lt;br /&gt;
    const mn_wrathBonus = 5;&lt;br /&gt;
    const mn_mitigationBonus = 5;&lt;br /&gt;
    const mn_averageDenominator = 2;&lt;br /&gt;
    const mn_diceNumberOfSides = 20;&lt;br /&gt;
    const mn_d20RollsArray = [&lt;br /&gt;
        0.1, 0.2, 0.3, 0.4, 0.5,&lt;br /&gt;
        0.6, 0.7, 0.8, 0.9, 1.0,&lt;br /&gt;
        1.1, 1.2, 1.3, 1.4, 1.5,&lt;br /&gt;
        1.6, 1.7, 1.8, 1.9, 2.0&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    const wrathRoll = Math.random() * (wrathInput + mn_wrathBonus);&lt;br /&gt;
    const mitigationRoll = Math.random() * (mitigationInput + mn_mitigationBonus);&lt;br /&gt;
&lt;br /&gt;
    var wrathMitRollAvg = (wrathInput + mitigationInput + mn_wrathBonus + mn_mitigationBonus) / mn_averageDenominator;&lt;br /&gt;
    if (wrathMitRollAvg &amp;lt; 1) {&lt;br /&gt;
        wrathMitRollAvg = 1;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var index = ((wrathRoll - mitigationRoll) + (wrathMitRollAvg / mn_averageDenominator));&lt;br /&gt;
    if (index &amp;lt; 0) {&lt;br /&gt;
        index = 0;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var rollIndex = Math.floor((index * mn_diceNumberOfSides) / wrathMitRollAvg);&lt;br /&gt;
    if (rollIndex &amp;gt; mn_diceNumberOfSides - 1) {&lt;br /&gt;
        rollIndex = mn_diceNumberOfSides - 1;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (rollIndex &amp;lt; 0) {&lt;br /&gt;
        rollIndex = 0;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return mn_d20RollsArray[rollIndex];&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function ApplyDamageTable(damageDoneInput, attackerStatStruct, booleansInput) {&lt;br /&gt;
    const mn_attackerDamageRounding = 0.5;&lt;br /&gt;
    const attackerDamageDone = damageDoneInput + mn_attackerDamageRounding;&lt;br /&gt;
    const attackerWrath = ComputeWrath(attackerStatStruct, booleansInput.bUseOffhand);&lt;br /&gt;
    const damageTable = GetDamageTable(attackerStatStruct.level);&lt;br /&gt;
&lt;br /&gt;
    if (!booleansInput.bForceMaxDamageInput &amp;amp;&amp;amp; (Math.random() * 100) &amp;lt;= damageTable.chance) {&lt;br /&gt;
        if (booleansInput.bUseOffhand) {&lt;br /&gt;
            return attackerDamageDone;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return attackerDamageDone + attackerStatStruct.mainHandDamageBonus;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    const mn_baseBonusDenominator = 2;&lt;br /&gt;
    const mn_percentBonus = 100;&lt;br /&gt;
    const mn_attackDamageDoneDenominator = 100;&lt;br /&gt;
    const mn_baseBonusMinimum = 10;&lt;br /&gt;
&lt;br /&gt;
    var baseBonus = (attackerWrath - damageTable.minusFactor) / mn_baseBonusDenominator;&lt;br /&gt;
    if (baseBonus &amp;lt; mn_baseBonusMinimum) {&lt;br /&gt;
        baseBonus = mn_baseBonusMinimum;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var extraPercent = Math.random() * baseBonus;&lt;br /&gt;
    if (booleansInput.bForceMaxDamageInput) {&lt;br /&gt;
        extraPercent = baseBonus;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var percent = mn_percentBonus + extraPercent;&lt;br /&gt;
    if (percent &amp;gt; damageTable.maxExtra) {&lt;br /&gt;
        percent = damageTable.maxExtra;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (booleansInput.bUseOffhand) {&lt;br /&gt;
        return ((attackerDamageDone * percent) / mn_attackDamageDoneDenominator);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return ((attackerDamageDone * percent) / mn_attackDamageDoneDenominator) + attackerStatStruct.mainHandDamageBonus;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function computeDoubleAttack(attackerStatStruct) {&lt;br /&gt;
    const mn_DoubleAttackDenominator = 500;&lt;br /&gt;
&lt;br /&gt;
    return (attackerStatStruct.doubleAttackSkill + attackerStatStruct.level) / mn_DoubleAttackDenominator;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function computeWeaponDelayWithHaste(attackerStatStruct, bUseOffhand) {&lt;br /&gt;
    const mn_WeaponDelayDenominator = 100;&lt;br /&gt;
&lt;br /&gt;
    var attackerWeaponDelay = attackerStatStruct.mainHandWeaponDelay;&lt;br /&gt;
    if (bUseOffhand) {&lt;br /&gt;
        attackerWeaponDelay = attackerStatStruct.offHandWeaponDelay;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return attackerWeaponDelay / (1 + (attackerStatStruct.hastePercentage / mn_WeaponDelayDenominator));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function computeDualWield(attackerStatStruct) {&lt;br /&gt;
    const mn_DualWieldDenominator = 400;&lt;br /&gt;
&lt;br /&gt;
    return (attackerStatStruct.dualWieldSkill + attackerStatStruct.level) / mn_DualWieldDenominator;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function computeRiposte(defenderStatStruct) {&lt;br /&gt;
    if (defenderStatStruct.riposteSkill &amp;lt;= 0) {&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var mn_riposteBonus = 100;&lt;br /&gt;
    var mn_riposteDenominator = 50;&lt;br /&gt;
&lt;br /&gt;
    return (Math.random() * 100) &amp;lt; (defenderStatStruct.riposteSkill + mn_riposteBonus) / mn_riposteDenominator;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function computeParry(defenderStatStruct) {&lt;br /&gt;
    if (defenderStatStruct.parrySkill &amp;lt;= 0) {&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var mn_parryBonus = 100;&lt;br /&gt;
    var mn_parryDenominator = 45;&lt;br /&gt;
&lt;br /&gt;
    return (Math.random() * 100) &amp;lt; (defenderStatStruct.parrySkill + mn_parryBonus) / mn_parryDenominator;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function computeDodge(defenderStatStruct) {&lt;br /&gt;
    if (defenderStatStruct.dodgeSkill &amp;lt;= 0) {&lt;br /&gt;
        return false;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var mn_dodgeBonus = 100;&lt;br /&gt;
    var mn_dodgeDenominator = 45;&lt;br /&gt;
&lt;br /&gt;
    return (Math.random() * 100) &amp;lt; (defenderStatStruct.dodgeSkill + mn_dodgeBonus) / mn_dodgeDenominator;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function rollHitAndDamageWithTable(attackerStatStruct, defenderStatStruct, bUseOffhand) {&lt;br /&gt;
    var returnStruct = {&lt;br /&gt;
        bHitDefender: false,&lt;br /&gt;
        diceRoll: 0,&lt;br /&gt;
        rolledDamage: 0,&lt;br /&gt;
        finalDamage: 0,&lt;br /&gt;
        riposted: false,&lt;br /&gt;
        parryed: false,&lt;br /&gt;
        dodged: false&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    // Order in EQEMU is Riposte &amp;gt; Parry &amp;gt; Dodge.&lt;br /&gt;
    // Parsing on P99 suggests Parry &amp;gt; Dodge &amp;gt; Riposte. Will use that instead for now.&lt;br /&gt;
&lt;br /&gt;
    if (computeParry(defenderStatStruct)) {&lt;br /&gt;
        returnStruct.parryed = true;&lt;br /&gt;
        return returnStruct;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (computeDodge(defenderStatStruct)) {&lt;br /&gt;
        returnStruct.dodged = true;&lt;br /&gt;
        return returnStruct;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (computeRiposte(defenderStatStruct)) {&lt;br /&gt;
        returnStruct.riposted = true;&lt;br /&gt;
        return returnStruct;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ((Math.random() * ComputeToHit(attackerStatStruct, bUseOffhand)) &amp;gt; (Math.random() * ComputeDefense(defenderStatStruct))) {&lt;br /&gt;
        returnStruct.bHitDefender = true;&lt;br /&gt;
        returnStruct.diceRoll = RollD20(ComputeWrath(attackerStatStruct, bUseOffhand), ComputeMitigation(defenderStatStruct));&lt;br /&gt;
        returnStruct.rolledDamage = attackerStatStruct.mainHandWeaponDamage * returnStruct.diceRoll;&lt;br /&gt;
        if (bUseOffhand) {&lt;br /&gt;
            returnStruct.rolledDamage = attackerStatStruct.offHandWeaponDamage * returnStruct.diceRoll;&lt;br /&gt;
        }&lt;br /&gt;
        returnStruct.finalDamage = Math.floor(ApplyDamageTable(returnStruct.rolledDamage, attackerStatStruct, { bForceMaxDamageInput: false, bUseOffhand: bUseOffhand }));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return returnStruct;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function RunDPSTest(attackerStatStruct, defenderStatStruct, numHitsToTestInput) {&lt;br /&gt;
    const mainHandDelayHasteAdjusted = computeWeaponDelayWithHaste(attackerStatStruct, false);&lt;br /&gt;
    const mainHandMaxDamage = Math.floor(ApplyDamageTable(attackerStatStruct.mainHandWeaponDamage * 2, attackerStatStruct, { bForceMaxDamageInput: true, bUseOffhand: false }));&lt;br /&gt;
&lt;br /&gt;
    var uniqueMainHandDamageValueCountArray = new Array(mainHandMaxDamage + 1).fill(0);&lt;br /&gt;
    var uniquediceRollCountArray = new Array(21).fill(0);&lt;br /&gt;
    var mainHandDamageAggregate = 0;&lt;br /&gt;
    var dualWieldAttempts = 0;&lt;br /&gt;
    var mainHandDoubleAttackAttempts = 0;&lt;br /&gt;
    var offHandDoubleAttackAttempts = 0;&lt;br /&gt;
    var ripostedCount = 0;&lt;br /&gt;
    var parriedCount = 0;&lt;br /&gt;
    var dodgedCount = 0;&lt;br /&gt;
    var i = 0;&lt;br /&gt;
    for (i = 0; i &amp;lt; numHitsToTestInput; i++) {&lt;br /&gt;
        const mainHandRollResult = rollHitAndDamageWithTable(attackerStatStruct, defenderStatStruct, false);&lt;br /&gt;
        if (mainHandRollResult.bHitDefender) {&lt;br /&gt;
            uniquediceRollCountArray[Math.floor(mainHandRollResult.diceRoll * 10)] += 1;&lt;br /&gt;
            uniqueMainHandDamageValueCountArray[mainHandRollResult.finalDamage] += 1;&lt;br /&gt;
&lt;br /&gt;
            mainHandDamageAggregate += mainHandRollResult.finalDamage;&lt;br /&gt;
        }&lt;br /&gt;
        else {&lt;br /&gt;
            uniquediceRollCountArray[0] += 1;&lt;br /&gt;
            uniqueMainHandDamageValueCountArray[0] += 1; // misses.&lt;br /&gt;
&lt;br /&gt;
            if (mainHandRollResult.riposted) {&lt;br /&gt;
                ripostedCount++;&lt;br /&gt;
            }&lt;br /&gt;
            else if (mainHandRollResult.parryed) {&lt;br /&gt;
                parriedCount++;&lt;br /&gt;
            }&lt;br /&gt;
            else if (mainHandRollResult.dodged) {&lt;br /&gt;
                dodgedCount++;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (attackerStatStruct.offHandWeaponDamage &amp;gt; 0 &amp;amp;&amp;amp; Math.random() &amp;lt;= computeDualWield(attackerStatStruct)) {&lt;br /&gt;
            dualWieldAttempts++;&lt;br /&gt;
&lt;br /&gt;
            if (attackerStatStruct.doubleAttackSkill &amp;gt; 0 &amp;amp;&amp;amp; Math.random() &amp;lt;= computeDoubleAttack(attackerStatStruct)) {&lt;br /&gt;
                offHandDoubleAttackAttempts++;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if (attackerStatStruct.doubleAttackSkill &amp;gt; 0 &amp;amp;&amp;amp; Math.random() &amp;lt;= computeDoubleAttack(attackerStatStruct)) {&lt;br /&gt;
            mainHandDoubleAttackAttempts++;&lt;br /&gt;
&lt;br /&gt;
            const mainHandRollResult = rollHitAndDamageWithTable(attackerStatStruct, defenderStatStruct, false);&lt;br /&gt;
            if (mainHandRollResult.bHitDefender) {&lt;br /&gt;
                uniquediceRollCountArray[Math.floor(mainHandRollResult.diceRoll * 10)] += 1;&lt;br /&gt;
                uniqueMainHandDamageValueCountArray[mainHandRollResult.finalDamage] += 1;&lt;br /&gt;
&lt;br /&gt;
                mainHandDamageAggregate += mainHandRollResult.finalDamage;&lt;br /&gt;
            }&lt;br /&gt;
            else {&lt;br /&gt;
                uniquediceRollCountArray[0] += 1;&lt;br /&gt;
                uniqueMainHandDamageValueCountArray[0] += 1; // misses.&lt;br /&gt;
&lt;br /&gt;
                if (mainHandRollResult.riposted) {&lt;br /&gt;
                    ripostedCount++;&lt;br /&gt;
                }&lt;br /&gt;
                else if (mainHandRollResult.parryed) {&lt;br /&gt;
                    parriedCount++;&lt;br /&gt;
                }&lt;br /&gt;
                else if (mainHandRollResult.dodged) {&lt;br /&gt;
                    dodgedCount++;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    const offHandDelayHasteAdjusted = computeWeaponDelayWithHaste(attackerStatStruct, true);&lt;br /&gt;
    const offHandMaxDamage = Math.floor(ApplyDamageTable(attackerStatStruct.offHandWeaponDamage * 2, attackerStatStruct, { bForceMaxDamageInput: true, bUseOffhand: true }));&lt;br /&gt;
    var offHandDamageAggregate = 0;&lt;br /&gt;
&lt;br /&gt;
    dualWieldAttempts = dualWieldAttempts / (offHandDelayHasteAdjusted / 10);&lt;br /&gt;
    offHandDoubleAttackAttempts = offHandDoubleAttackAttempts / (offHandDelayHasteAdjusted / 10);&lt;br /&gt;
&lt;br /&gt;
    const uniqueOffHandDamageValueCountArray = new Array(Math.floor(offHandMaxDamage) + 1).fill(0);&lt;br /&gt;
    for (i = 0; i &amp;lt; dualWieldAttempts + offHandDoubleAttackAttempts; i++) {&lt;br /&gt;
        const offHandRollResult = rollHitAndDamageWithTable(attackerStatStruct, defenderStatStruct, true);&lt;br /&gt;
        if (offHandRollResult.bHitDefender) {&lt;br /&gt;
            uniquediceRollCountArray[Math.floor(offHandRollResult.diceRoll * 10)] += 1;&lt;br /&gt;
            uniqueOffHandDamageValueCountArray[offHandRollResult.finalDamage] += 1;&lt;br /&gt;
&lt;br /&gt;
            offHandDamageAggregate += offHandRollResult.finalDamage;&lt;br /&gt;
        }&lt;br /&gt;
        else {&lt;br /&gt;
            uniquediceRollCountArray[0] += 1;&lt;br /&gt;
            uniqueOffHandDamageValueCountArray[0] += 1; // misses.&lt;br /&gt;
&lt;br /&gt;
            if (offHandRollResult.riposted) {&lt;br /&gt;
                ripostedCount++;&lt;br /&gt;
            }&lt;br /&gt;
            else if (offHandRollResult.parryed) {&lt;br /&gt;
                parriedCount++;&lt;br /&gt;
            }&lt;br /&gt;
            else if (offHandRollResult.dodged) {&lt;br /&gt;
                dodgedCount++;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    var diceRollAverage = 0;&lt;br /&gt;
    var diceRollCount = 0;&lt;br /&gt;
    for (i = 1; i &amp;lt; 21; i++) {&lt;br /&gt;
        diceRollAverage += uniquediceRollCountArray[i] * i;&lt;br /&gt;
        diceRollCount += uniquediceRollCountArray[i];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    diceRollAverage = diceRollAverage / diceRollCount;&lt;br /&gt;
&lt;br /&gt;
    return {&lt;br /&gt;
        &amp;quot;ApplyDamageTable() Main Hand Max Damge&amp;quot;: mainHandMaxDamage,&lt;br /&gt;
        &amp;quot;ApplyDamageTable() Off Hand Max Damage&amp;quot;: offHandMaxDamage,&lt;br /&gt;
        &amp;quot;Main Hand Swings&amp;quot;: numHitsToTestInput,&lt;br /&gt;
        &amp;quot;Main Hand Double Attack Swings&amp;quot;: Math.floor(mainHandDoubleAttackAttempts),&lt;br /&gt;
        &amp;quot;Dual Wield Swings&amp;quot;: Math.floor(dualWieldAttempts),&lt;br /&gt;
        &amp;quot;Dual Wield Double Attack Swings&amp;quot;: Math.floor(offHandDoubleAttackAttempts),&lt;br /&gt;
        &amp;quot;Dice Roll Average without zeros&amp;quot;: diceRollAverage,&lt;br /&gt;
        &amp;quot;Defender Riposte Count&amp;quot;: ripostedCount,&lt;br /&gt;
        &amp;quot;Defender Parry Count&amp;quot;: parriedCount,&lt;br /&gt;
        &amp;quot;Defender Dodged Count&amp;quot;: dodgedCount,&lt;br /&gt;
        &amp;quot;Unique Dice Roll Count&amp;quot;: uniquediceRollCountArray,&lt;br /&gt;
        &amp;quot;Main Hand Damage Aggregate&amp;quot;: mainHandDamageAggregate,&lt;br /&gt;
        &amp;quot;Uniquue Main Hand Damage Value Count&amp;quot;: uniqueMainHandDamageValueCountArray,&lt;br /&gt;
        &amp;quot;Off Hand Damage Aggregate&amp;quot;: offHandDamageAggregate,&lt;br /&gt;
        &amp;quot;Unique Off-Hand Damage Value Count&amp;quot;: uniqueOffHandDamageValueCountArray,&lt;br /&gt;
        &amp;quot;Predicted Total DPS&amp;quot;: (((mainHandDamageAggregate / numHitsToTestInput) / (mainHandDelayHasteAdjusted / 10)) + (offHandDamageAggregate / numHitsToTestInput))&lt;br /&gt;
    }&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>imported&gt;Loramin</name></author>
	</entry>
</feed>