2021-01-10 10:02:23 +00:00
const baseDamageMultiplier = [ 0.51 , 0.83 , 1.5 , 2.05 , 2.5 , 3.1 , 4.3 ] ;
const attackSpeeds = [ "SUPER_SLOW" , "VERY_SLOW" , "SLOW" , "NORMAL" , "FAST" , "VERY_FAST" , "SUPER_FAST" ] ;
2021-01-12 22:49:57 +00:00
const classDefenseMultipliers = new Map ( [ [ "relik" , 0.50 ] , [ "bow" , 0.60 ] , [ "wand" , 0.80 ] , [ "dagger" , 1.0 ] , [ "spear" , 1.20 ] ] ) ;
2021-01-10 10:02:23 +00:00
2021-01-07 00:08:19 +00:00
/ * T u r n s t h e i n p u t a m o u n t o f s k i l l p o i n t s i n t o a f l o a t p r e c i s i o n p e r c e n t a g e .
* @ param skp - the integer skillpoint count to be converted
* /
function skillPointsToPercentage ( skp ) {
if ( skp <= 0 ) {
return 0.0 ;
} else if ( skp >= 150 ) {
return 0.808 ;
} else {
2021-01-15 08:23:55 +00:00
return ( - 0.0000000066695 * Math . pow ( Math . E , - 0.00924033 * skp + 18.9 ) + 1.0771 ) ;
2021-01-07 00:08:19 +00:00
//return(-0.0000000066695* Math.pow(Math.E, -0.00924033 * skp + 18.9) + 1.0771).toFixed(3);
}
}
/ * T u r n s t h e i n p u t a m o u n t o f l e v e l s i n t o s k i l l p o i n t s a v a i l a b l e .
*
* @ param level - the integer level count te be converted
* /
function levelToSkillPoints ( level ) {
if ( level < 1 ) {
return 0 ;
} else if ( level >= 101 ) {
return 200 ;
} else {
return ( level - 1 ) * 2 ;
}
}
/ * T u r n s t h e i n p u t a m o u n t o f l e v e l s i n t o b a s e H P .
* @ param level - the integer level count to be converted
* /
function levelToHPBase ( level ) {
if ( level < 1 ) { //bad level
return this . levelToHPBase ( 1 ) ;
} else if ( level > 106 ) { //also bad level
return this . levelToHPBase ( 106 ) ;
} else { //good level
return 5 * level + 5 ;
}
}
2021-01-07 00:02:10 +00:00
/ * C l a s s t h a t r e p r e s e n t s a w y n n p l a y e r ' s b u i l d .
* /
class Build {
2021-01-09 08:52:58 +00:00
/ *
* Construct a build .
* @ param level : Level of the player .
* @ param equipment : List of equipment names that make up the build .
* In order : Helmet , Chestplate , Leggings , Boots , Ring1 , Ring2 , Brace , Neck , Weapon .
* @ param powders : Powder application . List of lists of integers ( powder IDs ) .
* In order : Helmet , Chestplate , Leggings , Boots , Weapon .
* /
2021-01-14 03:17:01 +00:00
constructor ( level , equipment , powders , externalStats ) {
2021-01-08 20:17:37 +00:00
// NOTE: powders is just an array of arrays of powder IDs. Not powder objects.
2021-01-09 19:50:36 +00:00
this . powders = powders ;
2021-01-09 03:53:57 +00:00
if ( itemMap . get ( equipment [ 0 ] ) && itemMap . get ( equipment [ 0 ] ) . type === "helmet" ) {
const helmet = itemMap . get ( equipment [ 0 ] ) ;
2021-01-08 20:17:37 +00:00
this . powders [ 0 ] = this . powders [ 0 ] . slice ( 0 , helmet . slots ) ;
2021-01-09 00:56:07 +00:00
this . helmet = expandItem ( helmet , this . powders [ 0 ] ) ;
2021-01-07 00:02:10 +00:00
} else {
2021-01-12 23:14:12 +00:00
throw new TypeError ( "No such helmet named " + equipment [ 0 ] ) ;
2021-01-09 03:53:57 +00:00
}
2021-01-12 23:14:12 +00:00
if ( itemMap . get ( equipment [ 1 ] ) && itemMap . get ( equipment [ 1 ] ) . type === "chestplate" ) {
2021-01-09 03:53:57 +00:00
const chestplate = itemMap . get ( equipment [ 1 ] ) ;
2021-01-08 20:17:37 +00:00
this . powders [ 1 ] = this . powders [ 1 ] . slice ( 0 , chestplate . slots ) ;
2021-01-09 00:56:07 +00:00
this . chestplate = expandItem ( chestplate , this . powders [ 1 ] ) ;
2021-01-07 00:02:10 +00:00
} else {
2021-01-12 23:14:12 +00:00
throw new TypeError ( "No such chestplate named " + equipment [ 1 ] ) ;
2021-01-09 03:53:57 +00:00
}
2021-01-12 23:14:12 +00:00
if ( itemMap . get ( equipment [ 2 ] ) && itemMap . get ( equipment [ 2 ] ) . type === "leggings" ) {
2021-01-09 03:53:57 +00:00
const leggings = itemMap . get ( equipment [ 2 ] ) ;
2021-01-08 20:17:37 +00:00
this . powders [ 2 ] = this . powders [ 2 ] . slice ( 0 , leggings . slots ) ;
2021-01-09 00:56:07 +00:00
this . leggings = expandItem ( leggings , this . powders [ 2 ] ) ;
2021-01-07 00:02:10 +00:00
} else {
2021-01-12 23:14:12 +00:00
throw new TypeError ( "No such leggings named " + equipment [ 2 ] ) ;
2021-01-09 03:53:57 +00:00
}
2021-01-12 23:14:12 +00:00
if ( itemMap . get ( equipment [ 3 ] ) && itemMap . get ( equipment [ 3 ] ) . type === "boots" ) {
2021-01-09 03:53:57 +00:00
const boots = itemMap . get ( equipment [ 3 ] ) ;
2021-01-08 20:17:37 +00:00
this . powders [ 3 ] = this . powders [ 3 ] . slice ( 0 , boots . slots ) ;
2021-01-09 00:56:07 +00:00
this . boots = expandItem ( boots , this . powders [ 3 ] ) ;
2021-01-09 03:53:57 +00:00
} else {
2021-01-12 23:14:12 +00:00
throw new TypeError ( "No such boots named " + equipment [ 3 ] ) ;
2021-01-07 00:02:10 +00:00
}
2021-01-12 23:14:12 +00:00
if ( itemMap . get ( equipment [ 4 ] ) && itemMap . get ( equipment [ 4 ] ) . type === "ring" ) {
2021-01-09 03:53:57 +00:00
const ring = itemMap . get ( equipment [ 4 ] ) ;
2021-01-09 19:50:36 +00:00
this . ring1 = expandItem ( ring , [ ] ) ;
2021-01-07 00:02:10 +00:00
} else {
2021-01-12 23:14:12 +00:00
throw new TypeError ( "No such ring named " + equipment [ 4 ] ) ;
2021-01-07 00:02:10 +00:00
}
2021-01-12 23:14:12 +00:00
if ( itemMap . get ( equipment [ 5 ] ) && itemMap . get ( equipment [ 5 ] ) . type === "ring" ) {
2021-01-09 03:53:57 +00:00
const ring = itemMap . get ( equipment [ 5 ] ) ;
2021-01-09 19:50:36 +00:00
this . ring2 = expandItem ( ring , [ ] ) ;
2021-01-07 00:02:10 +00:00
} else {
2021-01-12 23:14:12 +00:00
throw new TypeError ( "No such ring named " + equipment [ 5 ] ) ;
2021-01-07 00:02:10 +00:00
}
2021-01-12 23:14:12 +00:00
if ( itemMap . get ( equipment [ 6 ] ) && itemMap . get ( equipment [ 6 ] ) . type === "bracelet" ) {
2021-01-09 03:53:57 +00:00
const bracelet = itemMap . get ( equipment [ 6 ] ) ;
2021-01-09 19:50:36 +00:00
this . bracelet = expandItem ( bracelet , [ ] ) ;
2021-01-07 00:02:10 +00:00
} else {
2021-01-12 23:14:12 +00:00
throw new TypeError ( "No such bracelet named " + equipment [ 6 ] ) ;
2021-01-07 00:02:10 +00:00
}
2021-01-12 23:14:12 +00:00
if ( itemMap . get ( equipment [ 7 ] ) && itemMap . get ( equipment [ 7 ] ) . type === "necklace" ) {
2021-01-09 03:53:57 +00:00
const necklace = itemMap . get ( equipment [ 7 ] ) ;
2021-01-09 19:50:36 +00:00
this . necklace = expandItem ( necklace , [ ] ) ;
2021-01-07 00:02:10 +00:00
} else {
2021-01-12 23:14:12 +00:00
throw new TypeError ( "No such necklace named " + equipment [ 7 ] ) ;
2021-01-07 00:02:10 +00:00
}
2021-01-12 23:14:12 +00:00
if ( itemMap . get ( equipment [ 8 ] ) && itemMap . get ( equipment [ 8 ] ) . category === "weapon" ) {
2021-01-09 03:53:57 +00:00
const weapon = itemMap . get ( equipment [ 8 ] ) ;
2021-01-08 20:17:37 +00:00
this . powders [ 4 ] = this . powders [ 4 ] . slice ( 0 , weapon . slots ) ;
2021-01-09 00:56:07 +00:00
this . weapon = expandItem ( weapon , this . powders [ 4 ] ) ;
2021-01-07 00:02:10 +00:00
} else {
2021-01-12 23:14:12 +00:00
throw new TypeError ( "No such weapon named " + equipment [ 8 ] ) ;
2021-01-07 00:02:10 +00:00
}
if ( level < 1 ) { //Should these be constants?
this . level = 1 ;
} else if ( level > 106 ) {
this . level = 106 ;
} else {
this . level = level ;
}
2021-01-07 06:41:41 +00:00
this . availableSkillpoints = levelToSkillPoints ( this . level ) ;
2021-01-09 00:56:07 +00:00
this . equipment = [ this . helmet , this . chestplate , this . leggings , this . boots , this . ring1 , this . ring2 , this . bracelet , this . necklace ] ;
this . items = this . equipment . concat ( [ this . weapon ] ) ;
2021-01-07 06:41:41 +00:00
// return [equip_order, best_skillpoints, final_skillpoints, best_total];
2021-01-09 00:56:07 +00:00
let result = calculate _skillpoints ( this . equipment , this . weapon ) ;
2021-01-07 06:41:41 +00:00
this . equip _order = result [ 0 ] ;
this . base _skillpoints = result [ 1 ] ;
this . total _skillpoints = result [ 2 ] ;
this . assigned _skillpoints = result [ 3 ] ;
2021-01-10 04:29:07 +00:00
this . activeSetCounts = result [ 4 ] ;
2021-01-14 03:17:01 +00:00
2021-01-08 20:17:37 +00:00
// For strength boosts like warscream, vanish, etc.
2021-01-09 00:56:07 +00:00
this . damageMultiplier = 1.0 ;
2021-01-14 06:58:32 +00:00
this . defenseMultiplier = 1.0 ;
2021-01-08 20:17:37 +00:00
2021-01-14 03:17:01 +00:00
// For other external boosts ;-;
this . externalStats = externalStats ;
2021-01-08 04:31:29 +00:00
this . initBuildStats ( ) ;
2021-01-07 00:02:10 +00:00
}
/ * R e t u r n s b u i l d i n s t r i n g f o r m a t
2021-01-07 06:41:41 +00:00
* /
2021-01-07 00:02:10 +00:00
toString ( ) {
2021-01-09 00:56:07 +00:00
return this . helmet . get ( "name" ) + ", " + this . chestplate . get ( "name" ) + ", " + this . leggings . get ( "name" ) + ", " + this . boots . get ( "name" ) + ", " + this . ring1 . get ( "name" ) + ", " + this . ring2 . get ( "name" ) + ", " + this . bracelet . get ( "name" ) + ", " + this . necklace . get ( "name" ) + ", " + this . weapon . get ( "name" ) ;
2021-01-07 00:02:10 +00:00
}
2021-01-07 06:41:41 +00:00
/* Getters */
2021-01-07 21:32:36 +00:00
/ * G e t t o t a l h e a l t h f o r b u i l d .
* /
2021-01-09 08:52:58 +00:00
getSpellCost ( spellIdx , cost ) {
cost = Math . ceil ( cost * ( 1 - skillPointsToPercentage ( this . total _skillpoints [ 2 ] ) ) ) ;
cost += this . statMap . get ( "spRaw" + spellIdx ) ;
2021-01-15 18:49:24 +00:00
return Math . max ( 1 , Math . floor ( cost * ( 1 + this . statMap . get ( "spPct" + spellIdx ) / 100 ) ) ) ;
2021-01-09 08:52:58 +00:00
}
2021-01-09 00:56:07 +00:00
2021-01-08 02:34:07 +00:00
/ * G e t m e l e e s t a t s f o r b u i l d .
Returns an array in the order :
2021-01-07 21:32:36 +00:00
* /
2021-01-08 02:34:07 +00:00
getMeleeStats ( ) {
2021-01-08 05:36:57 +00:00
const stats = this . statMap ;
let adjAtkSpd = attackSpeeds . indexOf ( stats . get ( "atkSpd" ) ) + stats . get ( "atkTier" ) ;
if ( adjAtkSpd > 6 ) {
adjAtkSpd = 6 ;
} else if ( adjAtkSpd < 0 ) {
adjAtkSpd = 0 ;
}
2021-01-09 00:56:07 +00:00
// 0 for melee damage.
2021-01-14 03:17:01 +00:00
let results = calculateSpellDamage ( stats , [ 100 , 0 , 0 , 0 , 0 , 0 ] , stats . get ( "mdRaw" ) , stats . get ( "mdPct" ) + this . externalStats . get ( "mdPct" ) , 0 , this . weapon , this . total _skillpoints , this . damageMultiplier , this . externalStats ) ;
2021-01-11 02:58:39 +00:00
let dex = this . total _skillpoints [ 1 ] ;
2021-01-09 03:53:57 +00:00
2021-01-09 00:56:07 +00:00
let totalDamNorm = results [ 0 ] ;
let totalDamCrit = results [ 1 ] ;
2021-01-11 02:58:39 +00:00
totalDamNorm . push ( 1 - skillPointsToPercentage ( dex ) ) ;
totalDamCrit . push ( skillPointsToPercentage ( dex ) ) ;
2021-01-09 00:56:07 +00:00
let damages _results = results [ 2 ] ;
2021-01-11 02:58:39 +00:00
2021-01-12 22:49:57 +00:00
let singleHitTotal = ( ( totalDamNorm [ 0 ] + totalDamNorm [ 1 ] ) * ( totalDamNorm [ 2 ] )
+ ( totalDamCrit [ 0 ] + totalDamCrit [ 1 ] ) * ( totalDamCrit [ 2 ] ) ) / 2 ;
2021-01-08 04:31:29 +00:00
2021-01-08 02:34:07 +00:00
//Now do math
2021-01-08 05:36:57 +00:00
let normDPS = ( totalDamNorm [ 0 ] + totalDamNorm [ 1 ] ) / 2 * baseDamageMultiplier [ adjAtkSpd ] ;
let critDPS = ( totalDamCrit [ 0 ] + totalDamCrit [ 1 ] ) / 2 * baseDamageMultiplier [ adjAtkSpd ] ;
2021-01-09 00:56:07 +00:00
let avgDPS = ( normDPS * ( 1 - skillPointsToPercentage ( dex ) ) ) + ( critDPS * ( skillPointsToPercentage ( dex ) ) ) ;
2021-01-12 22:49:57 +00:00
//[[n n n n] [e e e e] [t t t t] [w w w w] [f f f f] [a a a a] [lowtotal hightotal normalChance] [critlowtotal crithightotal critChance] normalDPS critCPS averageDPS adjAttackSpeed, singleHit]
return damages _results . concat ( [ totalDamNorm , totalDamCrit , normDPS , critDPS , avgDPS , adjAtkSpd , singleHitTotal ] ) ;
2021-01-07 21:32:36 +00:00
}
2021-01-10 10:02:23 +00:00
/ *
Get all defensive stats for this build .
* /
getDefenseStats ( ) {
const stats = this . statMap ;
let defenseStats = [ ] ;
let def _pct = skillPointsToPercentage ( this . total _skillpoints [ 3 ] ) ;
let agi _pct = skillPointsToPercentage ( this . total _skillpoints [ 4 ] ) ;
//total hp
let totalHp = stats . get ( "hp" ) + stats . get ( "hpBonus" ) ;
2021-01-10 12:54:25 +00:00
if ( totalHp < 5 ) totalHp = 5 ;
2021-01-10 10:02:23 +00:00
defenseStats . push ( totalHp ) ;
//EHP
2021-01-11 00:46:30 +00:00
let ehp = [ totalHp , totalHp ] ;
2021-01-10 10:02:23 +00:00
let defMult = classDefenseMultipliers . get ( this . weapon . get ( "type" ) ) ;
2021-01-14 06:58:32 +00:00
ehp [ 0 ] /= ( ( 1 - def _pct ) * ( 1 - agi _pct ) * ( 2 - defMult ) * ( 2 - this . defenseMultiplier ) ) ;
ehp [ 1 ] /= ( ( 1 - def _pct ) * ( 2 - defMult ) * ( 2 - this . defenseMultiplier ) ) ;
2021-01-10 10:02:23 +00:00
defenseStats . push ( ehp ) ;
//HPR
let totalHpr = rawToPct ( stats . get ( "hprRaw" ) , stats . get ( "hprPct" ) / 100. ) ;
defenseStats . push ( totalHpr ) ;
//EHPR
2021-01-11 00:46:30 +00:00
let ehpr = [ totalHpr , totalHpr ] ;
2021-01-14 06:58:32 +00:00
ehpr [ 0 ] /= ( ( 1 - def _pct ) * ( 1 - agi _pct ) * ( 2 - defMult ) * ( 2 - this . defenseMultiplier ) ) ;
ehpr [ 1 ] /= ( ( 1 - def _pct ) * ( 2 - defMult ) * ( 2 - this . defenseMultiplier ) ) ;
2021-01-10 10:02:23 +00:00
defenseStats . push ( ehpr ) ;
//skp stats
2021-01-14 06:58:32 +00:00
defenseStats . push ( [ ( 1 - ( ( 1 - def _pct ) * ( 2 - this . defenseMultiplier ) ) ) * 100 , agi _pct * 100 ] ) ;
2021-01-10 10:02:23 +00:00
//eledefs - TODO POWDERS
let eledefs = [ 0 , 0 , 0 , 0 , 0 ] ;
for ( const i in skp _elements ) { //kinda jank but ok
eledefs [ i ] = rawToPct ( stats . get ( skp _elements [ i ] + "Def" ) , stats . get ( skp _elements [ i ] + "DefPct" ) / 100. ) ;
}
defenseStats . push ( eledefs ) ;
2021-01-11 00:46:30 +00:00
//[total hp, [ehp w/ agi, ehp w/o agi], total hpr, [ehpr w/ agi, ehpr w/o agi], [def%, agi%], [edef,tdef,wdef,fdef,adef]]
2021-01-10 10:02:23 +00:00
return defenseStats ;
}
2021-01-08 05:36:57 +00:00
/ * G e t a l l s t a t s f o r t h i s b u i l d . S t o r e s i n t h i s . s t a t M a p .
2021-01-07 21:32:36 +00:00
@ pre The build itself should be valid . No checking of validity of pieces is done here .
* /
2021-01-08 04:31:29 +00:00
initBuildStats ( ) {
let staticIDs = [ "hp" , "eDef" , "tDef" , "wDef" , "fDef" , "aDef" ] ;
2021-01-07 21:32:36 +00:00
//Create a map of this build's stats
//This is universal for every possible build, so it's possible to move this elsewhere.
let statMap = new Map ( ) ;
2021-01-08 04:31:29 +00:00
for ( const staticID of staticIDs ) {
statMap . set ( staticID , 0 ) ;
2021-01-07 21:32:36 +00:00
}
2021-01-08 16:47:51 +00:00
statMap . set ( "hp" , levelToHPBase ( this . level ) ) ; //TODO: Add player base health
2021-01-14 06:58:32 +00:00
2021-01-09 00:56:07 +00:00
for ( const item of this . items ) {
2021-01-08 04:31:29 +00:00
for ( let [ id , value ] of item . get ( "maxRolls" ) ) {
statMap . set ( id , ( statMap . get ( id ) || 0 ) + value ) ;
}
for ( const staticID of staticIDs ) {
2021-01-10 13:18:53 +00:00
if ( item . get ( staticID ) ) {
statMap . set ( staticID , statMap . get ( staticID ) + item . get ( staticID ) ) ;
}
2021-01-07 21:32:36 +00:00
}
}
2021-01-10 04:29:07 +00:00
for ( const [ setName , count ] of this . activeSetCounts ) {
2021-01-10 03:40:15 +00:00
const bonus = sets [ setName ] . bonuses [ count - 1 ] ;
for ( const id in bonus ) {
2021-01-10 04:29:07 +00:00
if ( skp _order . includes ( id ) ) {
// pass. Don't include skillpoints in ids
}
else {
statMap . set ( id , ( statMap . get ( id ) || 0 ) + bonus [ id ] ) ;
}
2021-01-10 03:40:15 +00:00
}
}
2021-01-07 06:41:41 +00:00
2021-01-08 04:31:29 +00:00
// The stuff relevant for damage calculation!!! @ferricles
2021-01-09 00:56:07 +00:00
statMap . set ( "atkSpd" , this . weapon . get ( "atkSpd" ) ) ;
statMap . set ( "damageRaw" , [ this . weapon . get ( "nDam" ) , this . weapon . get ( "eDam" ) , this . weapon . get ( "tDam" ) , this . weapon . get ( "wDam" ) , this . weapon . get ( "fDam" ) , this . weapon . get ( "aDam" ) ] ) ;
2021-01-08 05:36:57 +00:00
statMap . set ( "damageBonus" , [ statMap . get ( "eDamPct" ) , statMap . get ( "tDamPct" ) , statMap . get ( "wDamPct" ) , statMap . get ( "fDamPct" ) , statMap . get ( "aDamPct" ) ] ) ;
statMap . set ( "defRaw" , [ statMap . get ( "eDam" ) , statMap . get ( "tDef" ) , statMap . get ( "wDef" ) , statMap . get ( "fDef" ) , statMap . get ( "aDef" ) ] ) ;
statMap . set ( "defBonus" , [ statMap . get ( "eDamPct" ) , statMap . get ( "tDefPct" ) , statMap . get ( "wDefPct" ) , statMap . get ( "fDefPct" ) , statMap . get ( "aDefPct" ) ] ) ;
2021-01-14 06:58:32 +00:00
statMap . set ( "poisonPct" , 100 ) ;
2021-01-14 03:17:01 +00:00
for ( const x of skp _elements ) {
this . externalStats . set ( x + "DamPct" , 0 ) ;
}
this . externalStats . set ( "mdPct" , 0 ) ;
this . externalStats . set ( "sdPct" , 0 ) ;
this . externalStats . set ( "damageBonus" , [ 0 , 0 , 0 , 0 , 0 ] ) ;
this . externalStats . set ( "defBonus" , [ 0 , 0 , 0 , 0 , 0 ] ) ;
2021-01-14 06:58:32 +00:00
this . externalStats . set ( "poisonPct" , 0 ) ;
2021-01-08 04:31:29 +00:00
this . statMap = statMap ;
}
2021-01-07 00:02:10 +00:00
}