2021-01-15 05:47:38 +00:00
const damageMultipliers = new Map ( [ [ "allytotem" , . 15 ] , [ "yourtotem" , . 35 ] , [ "vanish" , 0.80 ] , [ "warscream" , 0.10 ] , [ "bash" , 0.50 ] ] ) ;
2021-01-09 00:56:07 +00:00
// Calculate spell damage given a spell elemental conversion table, and a spell multiplier.
// If spell mult is 0, its melee damage and we don't multiply by attack speed.
2021-01-14 03:17:01 +00:00
// externalStats should be a map
function calculateSpellDamage ( stats , spellConversions , rawModifier , pctModifier , spellMultiplier , weapon , total _skillpoints , damageMultiplier , externalStats ) {
let buildStats = new Map ( stats ) ;
if ( externalStats ) { //if nothing is passed in, then this hopefully won't trigger
for ( const [ key , value ] of externalStats ) {
if ( typeof value === "number" ) {
buildStats . set ( key , buildStats . get ( key ) + value ) ;
} else if ( Array . isArray ( value ) ) {
arr = [ ] ;
for ( let i = 0 ; i < value . length ; i ++ ) {
arr [ i ] = buildStats . get ( key ) [ i ] + value [ i ] ;
}
buildStats . set ( key , arr ) ;
}
}
}
2021-01-09 00:56:07 +00:00
// Array of neutral + ewtfa damages. Each entry is a pair (min, max).
let damages = [ ] ;
2021-01-14 03:17:01 +00:00
for ( const damage _string of buildStats . get ( "damageRaw" ) ) {
2021-01-09 00:56:07 +00:00
const damage _vals = damage _string . split ( "-" ) . map ( Number ) ;
damages . push ( damage _vals ) ;
}
// Applying powder.
let neutralBase = damages [ 0 ] . slice ( ) ;
let neutralRemainingRaw = damages [ 0 ] ;
for ( let i = 0 ; i < 5 ; ++ i ) {
let conversionRatio = spellConversions [ i + 1 ] / 100 ;
let min _diff = Math . min ( neutralRemainingRaw [ 0 ] , conversionRatio * neutralBase [ 0 ] ) ;
let max _diff = Math . min ( neutralRemainingRaw [ 1 ] , conversionRatio * neutralBase [ 1 ] ) ;
damages [ i + 1 ] [ 0 ] = Math . floor ( damages [ i + 1 ] [ 0 ] + min _diff ) ;
damages [ i + 1 ] [ 1 ] = Math . floor ( damages [ i + 1 ] [ 1 ] + max _diff ) ;
neutralRemainingRaw [ 0 ] = Math . floor ( neutralRemainingRaw [ 0 ] - min _diff ) ;
neutralRemainingRaw [ 1 ] = Math . floor ( neutralRemainingRaw [ 1 ] - max _diff ) ;
}
2021-01-09 13:15:30 +00:00
//console.log(damages);
2021-01-09 00:56:07 +00:00
let rawBoosts = [ [ 0 , 0 ] , [ 0 , 0 ] , [ 0 , 0 ] , [ 0 , 0 ] , [ 0 , 0 ] ] ;
for ( const powderID of weapon . get ( "powders" ) ) {
const powder = powderStats [ powderID ] ;
// Bitwise to force conversion to integer (integer division).
const element = ( powderID / 6 ) | 0 ;
let conversionRatio = powder . convert / 100 ;
2021-01-11 22:25:55 +00:00
if ( neutralRemainingRaw [ 1 ] > 0 ) {
2021-01-09 00:56:07 +00:00
let min _diff = Math . min ( neutralRemainingRaw [ 0 ] , conversionRatio * neutralBase [ 0 ] ) ;
let max _diff = Math . min ( neutralRemainingRaw [ 1 ] , conversionRatio * neutralBase [ 1 ] ) ;
damages [ element + 1 ] [ 0 ] = Math . floor ( damages [ element + 1 ] [ 0 ] + min _diff ) ;
damages [ element + 1 ] [ 1 ] = Math . floor ( damages [ element + 1 ] [ 1 ] + max _diff ) ;
neutralRemainingRaw [ 0 ] = Math . floor ( neutralRemainingRaw [ 0 ] - min _diff ) ;
neutralRemainingRaw [ 1 ] = Math . floor ( neutralRemainingRaw [ 1 ] - max _diff ) ;
}
damages [ element + 1 ] [ 0 ] += powder . min ;
damages [ element + 1 ] [ 1 ] += powder . max ;
}
2021-01-12 07:11:20 +00:00
let damageMult = damageMultiplier ;
2021-01-11 02:58:39 +00:00
let melee = false ;
2021-01-09 00:56:07 +00:00
// If we are doing melee calculations:
if ( spellMultiplier == 0 ) {
spellMultiplier = 1 ;
2021-01-11 02:58:39 +00:00
melee = true ;
2021-01-09 00:56:07 +00:00
}
else {
2021-01-14 03:17:01 +00:00
damageMult *= spellMultiplier * baseDamageMultiplier [ attackSpeeds . indexOf ( buildStats . get ( "atkSpd" ) ) ] ;
2021-01-09 00:56:07 +00:00
}
2021-01-09 09:09:47 +00:00
//console.log(damages);
//console.log(damageMult);
2021-01-09 00:56:07 +00:00
2021-01-12 22:49:57 +00:00
rawModifier *= spellMultiplier * damageMultiplier ;
2021-01-11 02:58:39 +00:00
let totalDamNorm = [ 0 , 0 ] ;
let totalDamCrit = [ 0 , 0 ] ;
if ( ! melee ) {
totalDamNorm = [ rawModifier , rawModifier ] ;
totalDamCrit = [ rawModifier , rawModifier ] ;
}
2021-01-09 00:56:07 +00:00
let damages _results = [ ] ;
// 0th skillpoint is strength, 1st is dex.
let str = total _skillpoints [ 0 ] ;
let staticBoost = ( pctModifier / 100. ) + skillPointsToPercentage ( str ) ;
let skillBoost = [ 0 ] ;
for ( let i in total _skillpoints ) {
2021-01-14 03:17:01 +00:00
skillBoost . push ( skillPointsToPercentage ( total _skillpoints [ i ] ) + buildStats . get ( "damageBonus" ) [ i ] / 100. ) ;
2021-01-09 00:56:07 +00:00
}
for ( let i in damages ) {
2021-01-15 04:48:48 +00:00
let damageBoost = 1 + skillBoost [ i ] + staticBoost ;
2021-01-09 00:56:07 +00:00
damages _results . push ( [
2021-01-15 04:48:48 +00:00
Math . max ( damages [ i ] [ 0 ] * Math . max ( damageBoost , 0 ) * damageMult , 0 ) , // Normal min
Math . max ( damages [ i ] [ 1 ] * Math . max ( damageBoost , 0 ) * damageMult , 0 ) , // Normal max
Math . max ( damages [ i ] [ 0 ] * Math . max ( 1 + damageBoost , 0 ) * damageMult , 0 ) , // Crit min
Math . max ( damages [ i ] [ 1 ] * Math . max ( 1 + damageBoost , 0 ) * damageMult , 0 ) , // Crit max
2021-01-09 00:56:07 +00:00
] ) ;
totalDamNorm [ 0 ] += damages _results [ i ] [ 0 ] ;
totalDamNorm [ 1 ] += damages _results [ i ] [ 1 ] ;
totalDamCrit [ 0 ] += damages _results [ i ] [ 2 ] ;
totalDamCrit [ 1 ] += damages _results [ i ] [ 3 ] ;
}
2021-01-11 10:03:19 +00:00
if ( melee ) {
totalDamNorm [ 0 ] += Math . max ( rawModifier , - damages _results [ 0 ] [ 0 ] ) ;
totalDamNorm [ 1 ] += Math . max ( rawModifier , - damages _results [ 0 ] [ 1 ] ) ;
totalDamCrit [ 0 ] += Math . max ( rawModifier , - damages _results [ 0 ] [ 2 ] ) ;
totalDamCrit [ 1 ] += Math . max ( rawModifier , - damages _results [ 0 ] [ 3 ] ) ;
}
2021-01-09 03:53:57 +00:00
damages _results [ 0 ] [ 0 ] += rawModifier ;
damages _results [ 0 ] [ 1 ] += rawModifier ;
2021-01-09 08:52:58 +00:00
damages _results [ 0 ] [ 2 ] += rawModifier ;
damages _results [ 0 ] [ 3 ] += rawModifier ;
2021-01-11 10:03:19 +00:00
2021-01-09 23:31:14 +00:00
if ( totalDamNorm [ 0 ] < 0 ) totalDamNorm [ 0 ] = 0 ;
if ( totalDamNorm [ 1 ] < 0 ) totalDamNorm [ 1 ] = 0 ;
if ( totalDamCrit [ 0 ] < 0 ) totalDamCrit [ 0 ] = 0 ;
if ( totalDamCrit [ 1 ] < 0 ) totalDamCrit [ 1 ] = 0 ;
2021-01-09 00:56:07 +00:00
return [ totalDamNorm , totalDamCrit , damages _results ] ;
}
2021-01-09 08:52:58 +00:00
2021-01-12 07:11:20 +00:00
2021-01-09 08:52:58 +00:00
const spell _table = {
"wand" : [
{ title : "Heal" , cost : 6 , parts : [
2021-01-11 12:36:24 +00:00
{ subtitle : "First Pulse" , type : "heal" , strength : 0.12 } ,
{ subtitle : "Second and Third Pulses" , type : "heal" , strength : 0.06 } ,
{ subtitle : "Total Heal" , type : "heal" , strength : 0.24 , summary : true }
2021-01-09 08:52:58 +00:00
] } ,
{ title : "Teleport" , cost : 4 , parts : [
2021-01-10 22:16:22 +00:00
{ subtitle : "Total Damage" , type : "damage" , multiplier : 100 , conversion : [ 60 , 0 , 40 , 0 , 0 , 0 ] , summary : true } ,
2021-01-09 08:52:58 +00:00
] } ,
{ title : "Meteor" , cost : 8 , parts : [
2021-01-10 22:16:22 +00:00
{ subtitle : "Blast Damage" , type : "damage" , multiplier : 500 , conversion : [ 40 , 30 , 0 , 0 , 30 , 0 ] , summary : true } ,
2021-01-10 21:01:59 +00:00
{ subtitle : "Burn Damage" , type : "damage" , multiplier : 125 , conversion : [ 100 , 0 , 0 , 0 , 0 , 0 ] } ,
2021-01-09 08:52:58 +00:00
] } ,
{ title : "Ice Snake" , cost : 4 , parts : [
2021-01-10 22:16:22 +00:00
{ subtitle : "Total Damage" , type : "damage" , multiplier : 70 , conversion : [ 50 , 0 , 0 , 50 , 0 , 0 ] , summary : true } ,
2021-01-09 08:52:58 +00:00
] } ,
] ,
"spear" : [
{ title : "Bash" , cost : 6 , parts : [
{ subtitle : "First Damage" , type : "damage" , multiplier : 130 , conversion : [ 60 , 40 , 0 , 0 , 0 , 0 ] } ,
{ subtitle : "Explosion Damage" , type : "damage" , multiplier : 130 , conversion : [ 100 , 0 , 0 , 0 , 0 , 0 ] } ,
2021-01-10 22:16:22 +00:00
{ subtitle : "Total Damage" , type : "total" , factors : [ 1 , 1 ] , summary : true } ,
2021-01-09 08:52:58 +00:00
] } ,
{ title : "Charge" , cost : 4 , parts : [
2021-01-10 22:16:22 +00:00
{ subtitle : "Total Damage" , type : "damage" , multiplier : 150 , conversion : [ 60 , 0 , 0 , 0 , 40 , 0 ] , summary : true } ,
2021-01-09 08:52:58 +00:00
] } ,
2021-01-21 18:30:22 +00:00
{ title : "Uppercut" , cost : 9 , parts : [
2021-01-09 08:52:58 +00:00
{ subtitle : "First Damage" , type : "damage" , multiplier : 300 , conversion : [ 70 , 20 , 10 , 0 , 0 , 0 ] } ,
{ subtitle : "Fireworks Damage" , type : "damage" , multiplier : 50 , conversion : [ 60 , 0 , 40 , 0 , 0 , 0 ] } ,
{ subtitle : "Crash Damage" , type : "damage" , multiplier : 50 , conversion : [ 80 , 0 , 20 , 0 , 0 , 0 ] } ,
2021-01-10 22:16:22 +00:00
{ subtitle : "Total Damage" , type : "total" , factors : [ 1 , 1 , 1 ] , summary : true } ,
2021-01-09 08:52:58 +00:00
] } ,
{ title : "War Scream" , cost : 6 , parts : [
2021-01-10 22:16:22 +00:00
{ subtitle : "Area Damage" , type : "damage" , multiplier : 50 , conversion : [ 0 , 0 , 0 , 0 , 75 , 25 ] , summary : true } ,
2021-01-09 08:52:58 +00:00
{ subtitle : "Air Shout (Per Hit)" , type : "damage" , multiplier : 30 , conversion : [ 0 , 0 , 0 , 0 , 75 , 25 ] } ,
] } ,
] ,
"bow" : [
{ title : "Arrow Storm" , cost : 6 , parts : [
2021-01-10 22:16:22 +00:00
{ subtitle : "Total Damage" , type : "damage" , multiplier : 600 , conversion : [ 60 , 0 , 25 , 0 , 15 , 0 ] , summary : true } ,
2021-01-09 08:52:58 +00:00
{ subtitle : "Per Arrow" , type : "damage" , multiplier : 10 , conversion : [ 60 , 0 , 25 , 0 , 15 , 0 ] } ,
] } ,
{ title : "Escape" , cost : 3 , parts : [
2021-01-10 22:16:22 +00:00
{ subtitle : "Landing Damage" , type : "damage" , multiplier : 100 , conversion : [ 50 , 0 , 0 , 0 , 0 , 50 ] , summary : true } ,
2021-01-09 08:52:58 +00:00
] } ,
{ title : "Bomb Arrow" , cost : 8 , parts : [
2021-01-10 22:16:22 +00:00
{ subtitle : "Total Damage" , type : "damage" , multiplier : 250 , conversion : [ 60 , 25 , 0 , 0 , 15 , 0 ] , summary : true } ,
2021-01-09 08:52:58 +00:00
] } ,
{ title : "Arrow Shield" , cost : 10 , parts : [
2021-01-10 22:16:22 +00:00
{ subtitle : "Shield Damage" , type : "damage" , multiplier : 100 , conversion : [ 70 , 0 , 0 , 0 , 0 , 30 ] , summary : true } ,
2021-01-09 08:52:58 +00:00
{ subtitle : "Arrow Rain Damage" , type : "damage" , multiplier : 200 , conversion : [ 70 , 0 , 0 , 0 , 0 , 30 ] } ,
] } ,
] ,
"dagger" : [
{ title : "Spin Attack" , cost : 6 , parts : [
2021-01-10 22:16:22 +00:00
{ subtitle : "Total Damage" , type : "damage" , multiplier : 150 , conversion : [ 70 , 0 , 30 , 0 , 0 , 0 ] , summary : true } ,
2021-01-09 08:52:58 +00:00
] } ,
2021-01-21 18:30:22 +00:00
{ title : "Vanish" , cost : 2 , parts : [
2021-01-10 22:16:22 +00:00
{ subtitle : "No Damage" , type : "none" , summary : true }
2021-01-09 08:52:58 +00:00
] } ,
{ title : "Multihit" , cost : 8 , parts : [
2021-01-09 09:35:18 +00:00
{ subtitle : "1st to 10th Hit" , type : "damage" , multiplier : 27 , conversion : [ 100 , 0 , 0 , 0 , 0 , 0 ] } ,
{ subtitle : "Fatality" , type : "damage" , multiplier : 120 , conversion : [ 20 , 0 , 30 , 50 , 0 , 0 ] } ,
2021-01-10 22:16:22 +00:00
{ subtitle : "Total Damage" , type : "total" , factors : [ 10 , 1 ] , summary : true } ,
2021-01-09 08:52:58 +00:00
] } ,
{ title : "Smoke Bomb" , cost : 8 , parts : [
{ subtitle : "Tick Damage" , type : "damage" , multiplier : 60 , conversion : [ 45 , 25 , 0 , 0 , 0 , 30 ] } ,
2021-01-10 22:16:22 +00:00
{ subtitle : "Total Damage" , type : "damage" , multiplier : 600 , conversion : [ 45 , 25 , 0 , 0 , 0 , 30 ] , summary : true } ,
2021-01-09 08:52:58 +00:00
] } ,
] ,
"relik" : [
{ title : "Totem" , cost : 4 , parts : [
{ subtitle : "Smash Damage" , type : "damage" , multiplier : 100 , conversion : [ 80 , 0 , 0 , 0 , 20 , 0 ] } ,
2021-01-09 11:01:57 +00:00
{ subtitle : "Damage Tick" , type : "damage" , multiplier : 20 , conversion : [ 80 , 0 , 0 , 0 , 0 , 20 ] } ,
2021-01-21 18:30:22 +00:00
{ subtitle : "Heal Tick" , type : "heal" , strength : 0.03 , summary : true } ,
2021-01-09 08:52:58 +00:00
] } ,
{ title : "Haul" , cost : 1 , parts : [
2021-01-10 22:16:22 +00:00
{ subtitle : "Total Damage" , type : "damage" , multiplier : 100 , conversion : [ 80 , 0 , 20 , 0 , 0 , 0 ] , summary : true } ,
2021-01-09 08:52:58 +00:00
] } ,
{ title : "Aura" , cost : 8 , parts : [
2021-01-10 22:16:22 +00:00
{ subtitle : "One Wave" , type : "damage" , multiplier : 200 , conversion : [ 70 , 0 , 0 , 30 , 0 , 0 ] , summary : true } ,
2021-01-09 08:52:58 +00:00
] } ,
{ title : "Uproot" , cost : 6 , parts : [
2021-01-21 18:30:22 +00:00
{ subtitle : "Total Damage" , type : "damage" , multiplier : 100 , conversion : [ 70 , 30 , 0 , 0 , 0 , 0 ] , summary : true } ,
2021-01-09 08:52:58 +00:00
] } ,
2021-01-12 22:49:02 +00:00
] ,
"powder" : [ //This is how instant-damage powder specials are implemented. To view time-boosted damage powder specials (curse, 2nd courage, air prison, view @TODO)
{ title : "Quake" , cost : 0 , parts : [
2021-01-13 08:30:55 +00:00
{ subtitle : "Total Damage" , type : "damage" , multiplier : [ 155 , 220 , 285 , 350 , 415 ] , conversion : [ 0 , 100 , 0 , 0 , 0 , 0 ] , summary : true } ,
2021-01-12 22:49:02 +00:00
] } ,
{ title : "Chain Lightning" , cost : 0 , parts : [
2021-01-13 08:30:55 +00:00
{ subtitle : "Total Damage" , type : "damage" , multiplier : [ 200 , 225 , 250 , 275 , 300 ] , conversion : [ 0 , 0 , 100 , 0 , 0 , 0 ] , summary : true } ,
2021-01-12 22:49:02 +00:00
] } ,
{ title : "Courage" , cost : 0 , parts : [
2021-01-13 08:30:55 +00:00
{ subtitle : "Total Damage" , type : "damage" , multiplier : [ 75 , 87.5 , 100 , 112.5 , 125 ] , conversion : [ 0 , 0 , 0 , 0 , 100 , 0 ] , summary : true } ,
2021-01-14 03:17:01 +00:00
] } , //[75, 87.5, 100, 112.5, 125]
2021-01-09 08:52:58 +00:00
]
2021-01-09 12:47:25 +00:00
} ;